// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include "chrome/browser/ash/extensions/autotest_private/autotest_private_api.h"
#include <deque>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <sstream>
#include <string_view>
#include <utility>
#include "ash/accessibility/accessibility_controller.h"
#include "ash/app_list/app_list_public_test_util.h"
#include "ash/components/arc/arc_prefs.h"
#include "ash/components/arc/metrics/arc_metrics_constants.h"
#include "ash/components/arc/mojom/power.mojom.h"
#include "ash/components/arc/mojom/system_ui.mojom-shared.h"
#include "ash/components/arc/session/arc_bridge_service.h"
#include "ash/components/arc/session/arc_service_manager.h"
#include "ash/components/arc/system_ui/arc_system_ui_bridge.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/ash_switches.h"
#include "ash/metrics/login_unlock_throughput_recorder.h"
#include "ash/public/cpp/accelerators.h"
#include "ash/public/cpp/ambient/ambient_prefs.h"
#include "ash/public/cpp/ambient/ambient_ui_model.h"
#include "ash/public/cpp/app_list/app_list_types.h"
#include "ash/public/cpp/autotest_ambient_api.h"
#include "ash/public/cpp/autotest_desks_api.h"
#include "ash/public/cpp/autotest_private_api_utils.h"
#include "ash/public/cpp/holding_space/holding_space_model.h"
#include "ash/public/cpp/holding_space/holding_space_prefs.h"
#include "ash/public/cpp/login_screen.h"
#include "ash/public/cpp/metrics_util.h"
#include "ash/public/cpp/overview_test_api.h"
#include "ash/public/cpp/shelf_item.h"
#include "ash/public/cpp/shelf_model.h"
#include "ash/public/cpp/shelf_prefs.h"
#include "ash/public/cpp/shelf_test_api.h"
#include "ash/public/cpp/shelf_types.h"
#include "ash/public/cpp/shelf_ui_info.h"
#include "ash/public/cpp/split_view_test_api.h"
#include "ash/public/cpp/tablet_mode.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/root_window_controller.h"
#include "ash/rotator/screen_rotation_animator.h"
#include "ash/shell.h"
#include "ash/style/dark_light_mode_controller_impl.h"
#include "ash/wallpaper/views/wallpaper_widget_controller.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/wm_event.h"
#include "base/base64.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/i18n/base_i18n_switches.h"
#include "base/json/json_reader.h"
#include "base/json/values_util.h"
#include "base/lazy_instance.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/field_trial.h"
#include "base/no_destructor.h"
#include "base/numerics/safe_conversions.h"
#include "base/ranges/algorithm.h"
#include "base/run_loop.h"
#include "base/scoped_observation.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/system/sys_info.h"
#include "base/task/bind_post_task.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.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/ash/app_list/arc/arc_app_list_prefs.h"
#include "chrome/browser/ash/app_list/arc/arc_app_utils.h"
#include "chrome/browser/ash/arc/arc_util.h"
#include "chrome/browser/ash/arc/session/arc_session_manager.h"
#include "chrome/browser/ash/arc/tracing/arc_app_performance_tracing.h"
#include "chrome/browser/ash/arc/tracing/arc_app_performance_tracing_session.h"
#include "chrome/browser/ash/assistant/assistant_util.h"
#include "chrome/browser/ash/borealis/borealis_installer.h"
#include "chrome/browser/ash/borealis/borealis_service.h"
#include "chrome/browser/ash/borealis/borealis_types.mojom.h"
#include "chrome/browser/ash/bruschetta/bruschetta_installer.h"
#include "chrome/browser/ash/bruschetta/bruschetta_service.h"
#include "chrome/browser/ash/bruschetta/bruschetta_util.h"
#include "chrome/browser/ash/crosapi/automation_ash.h"
#include "chrome/browser/ash/crosapi/browser_util.h"
#include "chrome/browser/ash/crosapi/crosapi_ash.h"
#include "chrome/browser/ash/crosapi/crosapi_manager.h"
#include "chrome/browser/ash/crostini/crostini_export_import.h"
#include "chrome/browser/ash/crostini/crostini_features.h"
#include "chrome/browser/ash/crostini/crostini_installer.h"
#include "chrome/browser/ash/crostini/crostini_manager.h"
#include "chrome/browser/ash/crostini/crostini_pref_names.h"
#include "chrome/browser/ash/crostini/crostini_util.h"
#include "chrome/browser/ash/file_manager/open_util.h"
#include "chrome/browser/ash/file_manager/path_util.h"
#include "chrome/browser/ash/fusebox/fusebox_server.h"
#include "chrome/browser/ash/guest_os/guest_os_registry_service.h"
#include "chrome/browser/ash/guest_os/guest_os_registry_service_factory.h"
#include "chrome/browser/ash/input_method/editor_mediator_factory.h"
#include "chrome/browser/ash/login/lock/screen_locker.h"
#include "chrome/browser/ash/login/wizard_context.h"
#include "chrome/browser/ash/plugin_vm/plugin_vm_installer.h"
#include "chrome/browser/ash/plugin_vm/plugin_vm_installer_factory.h"
#include "chrome/browser/ash/plugin_vm/plugin_vm_pref_names.h"
#include "chrome/browser/ash/plugin_vm/plugin_vm_util.h"
#include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
#include "chrome/browser/ash/policy/core/user_cloud_policy_manager_ash.h"
#include "chrome/browser/ash/power/ml/smart_dim/ml_agent.h"
#include "chrome/browser/ash/printing/cups_printers_manager.h"
#include "chrome/browser/ash/printing/cups_printers_manager_factory.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/ash/settings/stats_reporting_controller.h"
#include "chrome/browser/ash/system/input_device_settings.h"
#include "chrome/browser/ash/system_web_apps/system_web_app_manager.h"
#include "chrome/browser/banners/app_banner_manager_desktop.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/component_updater/smart_dim_component_installer.h"
#include "chrome/browser/extensions/component_loader.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/policy/chrome_policy_conversions_client.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/ui/ash/default_pinned_apps/default_pinned_apps.h"
#include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h"
#include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.h"
#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.h"
#include "chrome/browser/ui/ash/shelf/shelf_spinner_controller.h"
#include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
#include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/toolbar/app_menu_model.h"
#include "chrome/browser/ui/views/bruschetta/bruschetta_installer_view.h"
#include "chrome/browser/ui/views/crostini/crostini_uninstaller_view.h"
#include "chrome/browser/ui/views/plugin_vm/plugin_vm_installer_view.h"
#include "chrome/browser/ui/web_applications/web_app_dialogs.h"
#include "chrome/browser/ui/webui/ash/crostini_installer/crostini_installer_dialog.h"
#include "chrome/browser/ui/webui/ash/crostini_installer/crostini_installer_ui.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/common/extensions/api/autotest_private.h"
#include "chrome/common/pref_names.h"
#include "chromeos/ash/components/dbus/dbus_thread_manager.h"
#include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
#include "chromeos/ash/components/metrics/login_event_recorder.h"
#include "chromeos/ash/components/settings/cros_settings.h"
#include "chromeos/ash/components/settings/cros_settings_names.h"
#include "chromeos/ash/services/assistant/assistant_manager_service_impl.h"
#include "chromeos/ash/services/assistant/public/cpp/assistant_prefs.h"
#include "chromeos/ash/services/assistant/public/cpp/assistant_service.h"
#include "chromeos/components/quick_answers/public/cpp/quick_answers_prefs.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/printing/printer_configuration.h"
#include "chromeos/services/machine_learning/public/cpp/service_connection.h"
#include "chromeos/ui/base/app_types.h"
#include "chromeos/ui/base/window_properties.h"
#include "chromeos/ui/frame/caption_buttons/caption_button_model.h"
#include "chromeos/ui/frame/default_frame_header.h"
#include "chromeos/ui/frame/frame_header.h"
#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
#include "chromeos/ui/wm/desks/desks_helper.h"
#include "components/app_restore/full_restore_utils.h"
#include "components/app_restore/window_properties.h"
#include "components/policy/core/browser/policy_conversions.h"
#include "components/policy/core/common/cloud/cloud_policy_manager.h"
#include "components/policy/core/common/policy_service.h"
#include "components/policy/core/common/policy_types.h"
#include "components/policy/core/common/remote_commands/remote_commands_service.h"
#include "components/prefs/pref_service.h"
#include "components/services/app_service/public/cpp/app_types.h"
#include "components/services/app_service/public/cpp/types_util.h"
#include "components/session_manager/core/session_manager.h"
#include "components/session_manager/session_manager_types.h"
#include "components/signin/public/identity_manager/access_token_info.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/update_client/update_client_errors.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "components/variations/pref_names.h"
#include "components/viz/host/host_frame_sink_manager.h"
#include "components/webapps/browser/banners/app_banner_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_action.h"
#include "extensions/browser/extension_action_manager.h"
#include "extensions/browser/extension_function.h"
#include "extensions/browser/extension_function_registry.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/extension_util.h"
#include "extensions/common/api/extension_action/action_info.h"
#include "extensions/common/manifest_handlers/background_info.h"
#include "extensions/common/manifest_handlers/options_page_info.h"
#include "extensions/common/permissions/api_permission_set.h"
#include "extensions/common/permissions/permission_set.h"
#include "extensions/common/permissions/permissions_data.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "net/base/filename_util.h"
#include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom-shared.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/env.h"
#include "ui/aura/window.h"
#include "ui/aura/window_observer.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/ime/ash/ime_bridge.h"
#include "ui/base/ime/ash/input_method_manager.h"
#include "ui/base/ime/ash/text_input_method.h"
#include "ui/base/ui_base_features.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animator.h"
#include "ui/compositor/throughput_tracker.h"
#include "ui/compositor/throughput_tracker_host.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/events/event_constants.h"
#include "ui/events/types/event_type.h"
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/notification_list.h"
#include "ui/message_center/public/cpp/notification.h"
#include "ui/message_center/public/cpp/notification_types.h"
#include "ui/ozone/public/ozone_platform.h"
#include "ui/ozone/public/system_input_injector.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/caption_button_types.h"
#include "ui/wm/core/coordinate_conversion.h"
#include "ui/wm/core/cursor_manager.h"
#include "ui/wm/core/window_properties.h"
#include "ui/wm/core/window_util.h"
#include "ui/wm/public/activation_client.h"
#include "url/gurl.h"
using extensions::mojom::ManifestLocation;
namespace extensions {
namespace {
using chromeos::PrinterClass;
// Features used for testing `isFeatureEnabled`.
BASE_FEATURE(kEnabledFeatureForTest,
"EnabledFeatureForTest",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kDisabledFeatureForTest,
"DisabledFeatureForTest",
base::FEATURE_DISABLED_BY_DEFAULT);
constexpr char kCrostiniNotAvailableForCurrentUserError[] =
"Crostini is not available for the current user";
NOINLINE int AccessArray(const int arr[], const int* index) {
return arr[*index];
}
base::Value::List GetHostPermissions(const Extension* ext,
bool effective_perm) {
const PermissionsData* permissions_data = ext->permissions_data();
const URLPatternSet* pattern_set = nullptr;
URLPatternSet effective_hosts;
if (effective_perm) {
effective_hosts = permissions_data->GetEffectiveHostPermissions();
pattern_set = &effective_hosts;
} else {
pattern_set = &permissions_data->active_permissions().explicit_hosts();
}
base::Value::List permissions;
for (const auto& perm : *pattern_set) {
permissions.Append(perm.GetAsString());
}
return permissions;
}
base::Value::List GetAPIPermissions(const Extension* ext) {
base::Value::List permissions;
std::set<std::string> perm_list =
ext->permissions_data()->active_permissions().GetAPIsAsStrings();
for (const auto& perm : perm_list) {
permissions.Append(perm);
}
return permissions;
}
bool IsTestMode(content::BrowserContext* context) {
return AutotestPrivateAPI::GetFactoryInstance()->Get(context)->test_mode();
}
std::string ConvertToString(message_center::NotificationType type) {
switch (type) {
case message_center::NOTIFICATION_TYPE_SIMPLE:
case message_center::DEPRECATED_NOTIFICATION_TYPE_BASE_FORMAT:
return "simple";
case message_center::NOTIFICATION_TYPE_IMAGE:
return "image";
case message_center::NOTIFICATION_TYPE_MULTIPLE:
return "multiple";
case message_center::NOTIFICATION_TYPE_PROGRESS:
return "progress";
case message_center::NOTIFICATION_TYPE_CUSTOM:
return "custom";
case message_center::NOTIFICATION_TYPE_CONVERSATION:
return "conversation";
}
return "unknown";
}
base::Value::Dict MakeDictionaryFromNotification(
const message_center::Notification& notification) {
return base::Value::Dict()
.Set("id", notification.id())
.Set("type", ConvertToString(notification.type()))
.Set("title", notification.title())
.Set("message", notification.message())
.Set("priority", notification.priority())
.Set("progress", notification.progress());
}
std::string GetPrinterType(PrinterClass type) {
switch (type) {
case PrinterClass::kSaved:
return "configured";
case PrinterClass::kEnterprise:
return "enterprise";
case PrinterClass::kAutomatic:
return "automatic";
case PrinterClass::kDiscovered:
return "discovered";
default:
return "unknown";
}
}
api::autotest_private::ShelfItemType GetShelfItemType(ash::ShelfItemType type) {
switch (type) {
case ash::TYPE_PINNED_APP:
return api::autotest_private::ShelfItemType::kPinnedApp;
case ash::TYPE_BROWSER_SHORTCUT:
return api::autotest_private::ShelfItemType::kBrowserShortcut;
case ash::TYPE_APP:
return api::autotest_private::ShelfItemType::kApp;
case ash::TYPE_UNPINNED_BROWSER_SHORTCUT:
return api::autotest_private::ShelfItemType::kUnpinnedBrowserShortcut;
case ash::TYPE_DIALOG:
return api::autotest_private::ShelfItemType::kDialog;
case ash::TYPE_UNDEFINED:
return api::autotest_private::ShelfItemType::kNone;
}
NOTREACHED_IN_MIGRATION();
return api::autotest_private::ShelfItemType::kNone;
}
api::autotest_private::ShelfItemStatus GetShelfItemStatus(
ash::ShelfItemStatus status) {
switch (status) {
case ash::STATUS_CLOSED:
return api::autotest_private::ShelfItemStatus::kClosed;
case ash::STATUS_RUNNING:
return api::autotest_private::ShelfItemStatus::kRunning;
case ash::STATUS_ATTENTION:
return api::autotest_private::ShelfItemStatus::kAttention;
}
NOTREACHED_IN_MIGRATION();
return api::autotest_private::ShelfItemStatus::kNone;
}
api::autotest_private::AppType GetAppType(apps::AppType type) {
switch (type) {
case apps::AppType::kArc:
return api::autotest_private::AppType::kArc;
case apps::AppType::kBuiltIn:
return api::autotest_private::AppType::kBuiltIn;
case apps::AppType::kCrostini:
return api::autotest_private::AppType::kCrostini;
case apps::AppType::kChromeApp:
case apps::AppType::kExtension:
return api::autotest_private::AppType::kExtension;
case apps::AppType::kPluginVm:
return api::autotest_private::AppType::kPluginVm;
case apps::AppType::kWeb:
case apps::AppType::kSystemWeb:
return api::autotest_private::AppType::kWeb;
case apps::AppType::kUnknown:
return api::autotest_private::AppType::kNone;
case apps::AppType::kStandaloneBrowser:
return api::autotest_private::AppType::kStandaloneBrowser;
case apps::AppType::kRemote:
return api::autotest_private::AppType::kRemote;
case apps::AppType::kBorealis:
return api::autotest_private::AppType::kBorealis;
case apps::AppType::kBruschetta:
return api::autotest_private::AppType::kBruschetta;
case apps::AppType::kStandaloneBrowserExtension:
return api::autotest_private::AppType::kNone;
case apps::AppType::kStandaloneBrowserChromeApp:
return api::autotest_private::AppType::kExtension;
}
NOTREACHED_IN_MIGRATION();
return api::autotest_private::AppType::kNone;
}
api::autotest_private::AppInstallSource GetAppInstallSource(
apps::InstallReason install_reason) {
switch (install_reason) {
case apps::InstallReason::kUnknown:
return api::autotest_private::AppInstallSource::kUnknown;
case apps::InstallReason::kSystem:
return api::autotest_private::AppInstallSource::kSystem;
case apps::InstallReason::kPolicy:
return api::autotest_private::AppInstallSource::kPolicy;
case apps::InstallReason::kOem:
return api::autotest_private::AppInstallSource::kOem;
case apps::InstallReason::kDefault:
return api::autotest_private::AppInstallSource::kDefault;
case apps::InstallReason::kSync:
return api::autotest_private::AppInstallSource::kSync;
case apps::InstallReason::kUser:
return api::autotest_private::AppInstallSource::kUser;
case apps::InstallReason::kSubApp:
return api::autotest_private::AppInstallSource::kSubApp;
case apps::InstallReason::kKiosk:
return api::autotest_private::AppInstallSource::kKiosk;
case apps::InstallReason::kCommandLine:
return api::autotest_private::AppInstallSource::kCommandLine;
}
NOTREACHED_IN_MIGRATION();
return api::autotest_private::AppInstallSource::kNone;
}
api::autotest_private::AppWindowType GetAppWindowType(chromeos::AppType type) {
switch (type) {
case chromeos::AppType::ARC_APP:
return api::autotest_private::AppWindowType::kArcApp;
case chromeos::AppType::SYSTEM_APP:
return api::autotest_private::AppWindowType::kSystemApp;
case chromeos::AppType::CROSTINI_APP:
return api::autotest_private::AppWindowType::kCrostiniApp;
case chromeos::AppType::CHROME_APP:
return api::autotest_private::AppWindowType::kExtensionApp;
case chromeos::AppType::BROWSER:
return api::autotest_private::AppWindowType::kBrowser;
case chromeos::AppType::LACROS:
return api::autotest_private::AppWindowType::kLacros;
case chromeos::AppType::NON_APP:
return api::autotest_private::AppWindowType::kNone;
// TODO(oshima): Investigate if we want to have "extension" type.
}
NOTREACHED_IN_MIGRATION();
return api::autotest_private::AppWindowType::kNone;
}
api::autotest_private::AppReadiness GetAppReadiness(apps::Readiness readiness) {
switch (readiness) {
case apps::Readiness::kReady:
return api::autotest_private::AppReadiness::kReady;
case apps::Readiness::kDisabledByBlocklist:
return api::autotest_private::AppReadiness::kDisabledByBlacklist;
case apps::Readiness::kDisabledByPolicy:
return api::autotest_private::AppReadiness::kDisabledByPolicy;
case apps::Readiness::kDisabledByUser:
return api::autotest_private::AppReadiness::kDisabledByUser;
case apps::Readiness::kTerminated:
return api::autotest_private::AppReadiness::kTerminated;
case apps::Readiness::kUninstalledByUser:
return api::autotest_private::AppReadiness::kUninstalledByUser;
case apps::Readiness::kRemoved:
return api::autotest_private::AppReadiness::kRemoved;
case apps::Readiness::kUninstalledByNonUser:
return api::autotest_private::AppReadiness::kUninstalledByMigration;
case apps::Readiness::kDisabledByLocalSettings:
return api::autotest_private::AppReadiness::kDisabledByLocalSettings;
case apps::Readiness::kUnknown:
return api::autotest_private::AppReadiness::kNone;
}
NOTREACHED_IN_MIGRATION();
return api::autotest_private::AppReadiness::kNone;
}
api::autotest_private::HotseatState GetHotseatState(
ash::HotseatState hotseat_state) {
switch (hotseat_state) {
case ash::HotseatState::kNone:
return api::autotest_private::HotseatState::kNone;
case ash::HotseatState::kHidden:
return api::autotest_private::HotseatState::kHidden;
case ash::HotseatState::kShownClamshell:
return api::autotest_private::HotseatState::kShownClamShell;
case ash::HotseatState::kShownHomeLauncher:
return api::autotest_private::HotseatState::kShownHomeLauncher;
case ash::HotseatState::kExtended:
return api::autotest_private::HotseatState::kExtended;
}
NOTREACHED_IN_MIGRATION();
}
api::autotest_private::WakefulnessMode GetWakefulnessMode(
arc::mojom::WakefulnessMode mode) {
switch (mode) {
case arc::mojom::WakefulnessMode::ASLEEP:
return api::autotest_private::WakefulnessMode::kAsleep;
case arc::mojom::WakefulnessMode::AWAKE:
return api::autotest_private::WakefulnessMode::kAwake;
case arc::mojom::WakefulnessMode::DOZING:
return api::autotest_private::WakefulnessMode::kDozing;
case arc::mojom::WakefulnessMode::DREAMING:
return api::autotest_private::WakefulnessMode::kDreaming;
case arc::mojom::WakefulnessMode::UNKNOWN:
return api::autotest_private::WakefulnessMode::kUnknown;
}
NOTREACHED_IN_MIGRATION();
}
// Helper function to set allowed user pref based on |pref_name| with any
// specific pref validations. Returns error messages if any.
std::string SetAllowedPref(Profile* profile,
const std::string& pref_name,
const base::Value& value) {
// Special case for the preference that is stored in the "Local State"
// profile.
if (pref_name == prefs::kEnableAdbSideloadingRequested) {
DCHECK(value.is_bool());
g_browser_process->local_state()->Set(pref_name, value);
return std::string();
}
if (pref_name == variations::prefs::kVariationsCompressedSeed ||
pref_name == variations::prefs::kVariationsSeedSignature) {
DCHECK(value.is_string());
g_browser_process->local_state()->Set(pref_name, value);
return std::string();
}
if (pref_name == ash::assistant::prefs::kAssistantEnabled) {
if (!value.is_bool()) {
return "Invalid value type.";
}
// Validate the Assistant service allowed state.
ash::assistant::AssistantAllowedState allowed_state =
assistant::IsAssistantAllowedForProfile(profile);
if (allowed_state != ash::assistant::AssistantAllowedState::ALLOWED) {
return base::StringPrintf("Assistant not allowed - state: %d",
allowed_state);
}
} else if (pref_name == ash::assistant::prefs::kAssistantConsentStatus) {
if (!value.is_int()) {
return "Invalid value type.";
}
if (!profile->GetPrefs()->GetBoolean(
ash::assistant::prefs::kAssistantEnabled)) {
return "Unable to set the pref because Assistant has not been enabled.";
}
} else if (pref_name == ash::assistant::prefs::kAssistantContextEnabled ||
pref_name == ash::assistant::prefs::kAssistantHotwordEnabled) {
if (!value.is_bool()) {
return "Invalid value type.";
}
// Assistant service must be enabled first for those prefs to take effect.
if (!profile->GetPrefs()->GetBoolean(
ash::assistant::prefs::kAssistantEnabled)) {
return std::string(
"Unable to set the pref because Assistant has not been enabled.");
}
} else if (pref_name ==
ash::prefs::kAssistantNumSessionsWhereOnboardingShown) {
if (!value.is_int()) {
return "Invalid value type.";
}
} else if (pref_name == ash::prefs::kAccessibilitySpokenFeedbackEnabled) {
DCHECK(value.is_bool());
} else if (pref_name == ash::prefs::kAccessibilityVirtualKeyboardEnabled) {
DCHECK(value.is_bool());
} else if (pref_name == prefs::kDocumentScanAPITrustedExtensions) {
DCHECK(value.is_list());
} else if (pref_name == ash::prefs::kEnableAutoScreenLock) {
DCHECK(value.is_bool());
} else if (pref_name == prefs::kLanguagePreloadEngines) {
DCHECK(value.is_string());
} else if (pref_name == plugin_vm::prefs::kPluginVmCameraAllowed) {
DCHECK(value.is_bool());
} else if (pref_name == plugin_vm::prefs::kPluginVmMicAllowed) {
DCHECK(value.is_bool());
} else if (pref_name == plugin_vm::prefs::kPluginVmDataCollectionAllowed) {
DCHECK(value.is_bool());
} else if (pref_name == prefs::kPrintingAPIExtensionsAllowlist) {
DCHECK(value.is_list());
} else if (pref_name == quick_answers::prefs::kQuickAnswersEnabled) {
DCHECK(value.is_bool());
} else if (pref_name ==
quick_answers::prefs::kQuickAnswersDefinitionEnabled) {
DCHECK(value.is_bool());
} else if (pref_name ==
quick_answers::prefs::kQuickAnswersTranslationEnabled) {
DCHECK(value.is_bool());
} else if (pref_name ==
quick_answers::prefs::kQuickAnswersUnitConversionEnabled) {
DCHECK(value.is_bool());
} else if (pref_name == quick_answers::prefs::kQuickAnswersConsentStatus) {
DCHECK(value.is_int());
} else if (pref_name == arc::prefs::kArcShowResizeLockSplashScreenLimits) {
DCHECK(value.is_int());
} else {
return "The pref " + pref_name + " is not allowed.";
}
// Set value for the specified user pref after validation.
profile->GetPrefs()->Set(pref_name, value);
return std::string();
}
// Helper function to clear allowed user pref based on |pref_name|. Returns
// true on success or false if |pref_name| is not in the allowlist.
bool ClearAllowedPref(Profile* profile, const std::string& pref_name) {
if (pref_name != ash::ambient::prefs::kAmbientUiSettings) {
return false;
}
profile->GetPrefs()->ClearPref(pref_name);
return true;
}
// Returns the ARC app window that associates with |package_name|. Note there
// might be more than 1 windows that have the same package name. This function
// just returns the first window it finds.
aura::Window* GetArcAppWindow(const std::string& package_name) {
for (auto* window : ChromeShelfController::instance()->GetArcWindows()) {
std::string* pkg_name = window->GetProperty(ash::kArcPackageNameKey);
if (pkg_name && *pkg_name == package_name) {
return window;
}
}
return nullptr;
}
// Gets expected window state type according to |event_type|.
chromeos::WindowStateType GetExpectedWindowState(
api::autotest_private::WMEventType event_type) {
switch (event_type) {
case api::autotest_private::WMEventType::kWmeventNormal:
return chromeos::WindowStateType::kNormal;
case api::autotest_private::WMEventType::kWmeventMaximize:
return chromeos::WindowStateType::kMaximized;
case api::autotest_private::WMEventType::kWmeventMinimize:
return chromeos::WindowStateType::kMinimized;
case api::autotest_private::WMEventType::kWmeventFullscreen:
return chromeos::WindowStateType::kFullscreen;
case api::autotest_private::WMEventType::kWmeventSnapPrimary:
return chromeos::WindowStateType::kPrimarySnapped;
case api::autotest_private::WMEventType::kWmeventSnapSecondary:
return chromeos::WindowStateType::kSecondarySnapped;
case api::autotest_private::WMEventType::kWmeventFloat:
return chromeos::WindowStateType::kFloated;
default:
NOTREACHED_IN_MIGRATION();
return chromeos::WindowStateType::kNormal;
}
}
ash::WMEventType ToWMEventType(api::autotest_private::WMEventType event_type) {
switch (event_type) {
case api::autotest_private::WMEventType::kWmeventNormal:
return ash::WMEventType::WM_EVENT_NORMAL;
case api::autotest_private::WMEventType::kWmeventMaximize:
return ash::WMEventType::WM_EVENT_MAXIMIZE;
case api::autotest_private::WMEventType::kWmeventMinimize:
return ash::WMEventType::WM_EVENT_MINIMIZE;
case api::autotest_private::WMEventType::kWmeventFullscreen:
return ash::WMEventType::WM_EVENT_FULLSCREEN;
case api::autotest_private::WMEventType::kWmeventSnapPrimary:
return ash::WMEventType::WM_EVENT_SNAP_PRIMARY;
case api::autotest_private::WMEventType::kWmeventSnapSecondary:
return ash::WMEventType::WM_EVENT_SNAP_SECONDARY;
case api::autotest_private::WMEventType::kWmeventFloat:
return ash::WMEventType::WM_EVENT_FLOAT;
default:
NOTREACHED_IN_MIGRATION();
return ash::WMEventType::WM_EVENT_NORMAL;
}
}
api::autotest_private::WindowStateType ToWindowStateType(
chromeos::WindowStateType state_type) {
switch (state_type) {
// Consider adding DEFAULT type to idl.
case chromeos::WindowStateType::kDefault:
case chromeos::WindowStateType::kNormal:
return api::autotest_private::WindowStateType::kNormal;
case chromeos::WindowStateType::kMinimized:
return api::autotest_private::WindowStateType::kMinimized;
case chromeos::WindowStateType::kMaximized:
return api::autotest_private::WindowStateType::kMaximized;
case chromeos::WindowStateType::kFullscreen:
return api::autotest_private::WindowStateType::kFullscreen;
case chromeos::WindowStateType::kPrimarySnapped:
return api::autotest_private::WindowStateType::kPrimarySnapped;
case chromeos::WindowStateType::kSecondarySnapped:
return api::autotest_private::WindowStateType::kSecondarySnapped;
case chromeos::WindowStateType::kPinned:
return api::autotest_private::WindowStateType::kPinned;
case chromeos::WindowStateType::kTrustedPinned:
return api::autotest_private::WindowStateType::kTrustedPinned;
case chromeos::WindowStateType::kPip:
return api::autotest_private::WindowStateType::kPip;
case chromeos::WindowStateType::kFloated:
return api::autotest_private::WindowStateType::kFloated;
default:
NOTREACHED_IN_MIGRATION();
return api::autotest_private::WindowStateType::kNone;
}
}
std::string GetPngDataAsString(scoped_refptr<base::RefCountedMemory> png_data) {
// Base64 encode the result so we can return it as a string.
std::string base64_png(png_data->front(),
png_data->front() + png_data->size());
return base::Base64Encode(base64_png);
}
display::Display::Rotation ToRotation(
api::autotest_private::RotationType rotation) {
switch (rotation) {
case api::autotest_private::RotationType::kRotate0:
return display::Display::ROTATE_0;
case api::autotest_private::RotationType::kRotate90:
return display::Display::ROTATE_90;
case api::autotest_private::RotationType::kRotate180:
return display::Display::ROTATE_180;
case api::autotest_private::RotationType::kRotate270:
return display::Display::ROTATE_270;
case api::autotest_private::RotationType::kRotateAny:
case api::autotest_private::RotationType::kNone:
break;
}
NOTREACHED_IN_MIGRATION();
return display::Display::ROTATE_0;
}
api::autotest_private::Bounds ToBoundsDictionary(const gfx::Rect& bounds) {
api::autotest_private::Bounds result;
result.left = bounds.x();
result.top = bounds.y();
result.width = bounds.width();
result.height = bounds.height();
return result;
}
gfx::Rect ToRect(const api::autotest_private::Bounds& result) {
return gfx::Rect(result.left, result.top, result.width, result.height);
}
std::vector<api::autotest_private::Bounds> ToBoundsDictionaryList(
const std::vector<gfx::Rect>& items_bounds) {
std::vector<api::autotest_private::Bounds> bounds_list;
for (const gfx::Rect& bounds : items_bounds) {
bounds_list.push_back(ToBoundsDictionary(bounds));
}
return bounds_list;
}
api::autotest_private::Location ToLocationDictionary(const gfx::Point& point) {
api::autotest_private::Location result;
result.x = point.x();
result.y = point.y();
return result;
}
arc::mojom::ThemeStyleType ToThemeStyleType(
const api::autotest_private::ThemeStyle& theme) {
switch (theme) {
case api::autotest_private::ThemeStyle::kTonalSpot:
return arc::mojom::ThemeStyleType::TONAL_SPOT;
case api::autotest_private::ThemeStyle::kVibrant:
return arc::mojom::ThemeStyleType::VIBRANT;
case api::autotest_private::ThemeStyle::kExpressive:
return arc::mojom::ThemeStyleType::EXPRESSIVE;
case api::autotest_private::ThemeStyle::kSpritz:
return arc::mojom::ThemeStyleType::SPRITZ;
case api::autotest_private::ThemeStyle::kRainbow:
return arc::mojom::ThemeStyleType::RAINBOW;
case api::autotest_private::ThemeStyle::kFruitSalad:
return arc::mojom::ThemeStyleType::FRUIT_SALAD;
default:
return arc::mojom::ThemeStyleType::TONAL_SPOT;
}
}
aura::Window* FindAppWindowById(const int64_t id) {
auto list = ash::GetAppWindowList();
auto iter = base::ranges::find(list, id, &aura::Window::GetId);
if (iter == list.end()) {
return nullptr;
}
return *iter;
}
// Returns the first available Browser that is not a web app.
Browser* GetFirstRegularBrowser() {
const BrowserList* list = BrowserList::GetInstance();
const web_app::AppBrowserController* (Browser::*app_controller)() const =
&Browser::app_controller;
auto iter = base::ranges::find(*list, nullptr, app_controller);
if (iter == list->end()) {
return nullptr;
}
return *iter;
}
ash::AppListViewState ToAppListViewState(
api::autotest_private::LauncherStateType state) {
switch (state) {
case api::autotest_private::LauncherStateType::kClosed:
return ash::AppListViewState::kClosed;
case api::autotest_private::LauncherStateType::kFullscreenAllApps:
return ash::AppListViewState::kFullscreenAllApps;
case api::autotest_private::LauncherStateType::kFullscreenSearch:
return ash::AppListViewState::kFullscreenSearch;
case api::autotest_private::LauncherStateType::kNone:
break;
}
return ash::AppListViewState::kClosed;
}
ash::OverviewAnimationState ToOverviewAnimationState(
api::autotest_private::OverviewStateType state) {
switch (state) {
case api::autotest_private::OverviewStateType::kShown:
return ash::OverviewAnimationState::kEnterAnimationComplete;
case api::autotest_private::OverviewStateType::kHidden:
return ash::OverviewAnimationState::kExitAnimationComplete;
case api::autotest_private::OverviewStateType::kNone:
break;
}
NOTREACHED_IN_MIGRATION();
return ash::OverviewAnimationState::kExitAnimationComplete;
}
ui::KeyboardCode StringToKeyCode(const std::string& str) {
constexpr struct Map {
const char* str;
ui::KeyboardCode key_code;
} map[] = {
{"search", ui::VKEY_LWIN},
{"assistant", ui::VKEY_ASSISTANT},
};
DCHECK(base::IsStringASCII(str));
if (str.length() == 1) {
char c = str[0];
if (c >= 'a' && c <= 'z') {
return static_cast<ui::KeyboardCode>(static_cast<int>(ui::VKEY_A) +
(c - 'a'));
}
if (c >= '0' && c <= '9') {
return static_cast<ui::KeyboardCode>(static_cast<int>(ui::VKEY_0) +
(c - '0'));
}
} else {
for (auto& entry : map) {
if (str == entry.str) {
return entry.key_code;
}
}
}
NOTREACHED_IN_MIGRATION();
return ui::VKEY_A;
}
aura::Window* GetActiveWindow() {
std::vector<raw_ptr<aura::Window, VectorExperimental>> list =
ash::GetAppWindowList();
if (!list.size()) {
return nullptr;
}
return wm::GetActivationClient(list[0]->GetRootWindow())->GetActiveWindow();
}
bool IsFrameVisible(views::Widget* widget) {
views::NonClientFrameView* frame_view =
widget->non_client_view() ? widget->non_client_view()->frame_view()
: nullptr;
return frame_view && frame_view->GetEnabled() && frame_view->GetVisible();
}
void ConvertPointToHost(aura::Window* root_window, gfx::PointF* location) {
*location = root_window->GetHost()->GetRootTransform().MapPoint(*location);
}
int GetMouseEventFlags(api::autotest_private::MouseButton button) {
switch (button) {
case api::autotest_private::MouseButton::kLeft:
return ui::EF_LEFT_MOUSE_BUTTON;
case api::autotest_private::MouseButton::kRight:
return ui::EF_RIGHT_MOUSE_BUTTON;
case api::autotest_private::MouseButton::kMiddle:
return ui::EF_MIDDLE_MOUSE_BUTTON;
case api::autotest_private::MouseButton::kBack:
return ui::EF_BACK_MOUSE_BUTTON;
case api::autotest_private::MouseButton::kForward:
return ui::EF_FORWARD_MOUSE_BUTTON;
default:
NOTREACHED_IN_MIGRATION();
}
return ui::EF_NONE;
}
// Gets display id out of an optional DOMString display id argument. Returns
// false if optional display id is given but in bad format. Otherwise returns
// true and fills |display_id| with either the primary display id when the
// optional arg is not given or the parsed display id out of the arg
bool GetDisplayIdFromOptionalArg(const std::optional<std::string>& arg,
int64_t* display_id) {
if (arg && !arg->empty()) {
return base::StringToInt64(*arg, display_id);
}
*display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
return true;
}
class DisplaySmoothnessTracker {
public:
using ReportCallback = base::OnceCallback<void(
const cc::FrameSequenceMetrics::CustomReportData& frame_data,
std::vector<int>&& throughput)>;
DisplaySmoothnessTracker() = default;
DisplaySmoothnessTracker(const DisplaySmoothnessTracker&) = delete;
DisplaySmoothnessTracker& operator=(const DisplaySmoothnessTracker&) = delete;
~DisplaySmoothnessTracker() = default;
// Return true if tracking is started successfully.
bool Start(int64_t display_id,
base::TimeDelta throughput_interval,
ui::ThroughputTrackerHost::ReportCallback callback) {
auto* root_window = ash::Shell::GetRootWindowForDisplayId(display_id);
if (!root_window) {
return false;
}
start_time_ = base::TimeTicks::Now();
DCHECK(root_window_tracker_.windows().empty());
root_window_tracker_.Add(root_window);
tracker_ =
root_window->layer()->GetCompositor()->RequestNewThroughputTracker();
tracker_->Start(std::move(callback));
throughtput_timer_.Start(FROM_HERE, throughput_interval, this,
&DisplaySmoothnessTracker::OnThroughputTimerFired);
return true;
}
bool Stop(ReportCallback callback) {
stopping_ = true;
throughtput_timer_.Stop();
callback_ = std::move(callback);
return tracker_->Stop();
}
void CancelReport() { tracker_->CancelReport(); }
ReportCallback TakeCallback() { return std::move(callback_); }
std::vector<int> TakeThroughput() { return std::move(throughput_); }
bool stopping() const { return stopping_; }
bool has_error() const { return has_error_; }
base::TimeTicks start_time() const { return start_time_; }
private:
void OnThroughputTimerFired() {
auto windows = root_window_tracker_.windows();
if (windows.empty()) {
// RootWindow is gone. This could happen when display is reconfigured
// during the test run. Treat it as error since no meaningful smoothness
// data would be captured in such case.
LOG(ERROR) << "Unable to collect throughput because underlying "
"RootWindow is gone.";
has_error_ = true;
throughtput_timer_.Stop();
return;
}
DCHECK_EQ(windows.size(), 1u);
auto* root_window = windows[0].get();
throughput_.push_back(
100 - root_window->GetHost()->compositor()->GetPercentDroppedFrames());
}
aura::WindowTracker root_window_tracker_;
std::optional<ui::ThroughputTracker> tracker_;
ReportCallback callback_;
bool stopping_ = false;
bool has_error_ = false;
base::TimeTicks start_time_;
base::RepeatingTimer throughtput_timer_;
std::vector<int> throughput_;
};
using DisplaySmoothnessTrackers =
std::map<int64_t, std::unique_ptr<DisplaySmoothnessTracker>>;
DisplaySmoothnessTrackers* GetDisplaySmoothnessTrackers() {
static base::NoDestructor<DisplaySmoothnessTrackers> trackers;
return trackers.get();
}
// Forwards frame rate data to the callback for |display_id| and resets.
void ForwardFrameRateDataAndReset(
int64_t display_id,
const cc::FrameSequenceMetrics::CustomReportData& frame_data) {
auto* trackers = GetDisplaySmoothnessTrackers();
auto it = trackers->find(display_id);
DCHECK(it != trackers->end());
auto throughput = it->second->TakeThroughput();
// Moves the callback out and erases the mapping first to allow new tracking
// for |display_id| to start before |callback| run returns.
// See https://crbug.com/1098886.
auto callback = it->second->TakeCallback();
DCHECK(callback);
trackers->erase(it);
std::move(callback).Run(frame_data, std::move(throughput));
}
std::string ResolutionToString(
ash::assistant::AssistantInteractionResolution resolution) {
using ash::assistant::AssistantInteractionResolution;
switch (resolution) {
case AssistantInteractionResolution::kNormal:
return "kNormal";
case AssistantInteractionResolution::kError:
return "kError";
case AssistantInteractionResolution::kInterruption:
return "kInterruption";
case AssistantInteractionResolution::kMicTimeout:
return "kMicTimeout";
case AssistantInteractionResolution::kMultiDeviceHotwordLoss:
return "kMultiDeviceHotwordLoss";
}
// Not reachable here.
DCHECK(false);
}
std::string CompositorFrameSinkTypeToString(
viz::mojom::CompositorFrameSinkType type) {
switch (type) {
case viz::mojom::CompositorFrameSinkType::kUnspecified:
return "unspecified";
case viz::mojom::CompositorFrameSinkType::kVideo:
return "video";
case viz::mojom::CompositorFrameSinkType::kMediaStream:
return "media-stream";
case viz::mojom::CompositorFrameSinkType::kLayerTree:
return "layer-tree";
}
}
// Update when `startThroughputTrackerDataCollection` is called.
base::TimeTicks g_last_start_throughput_data_collection_tick;
} // namespace
class WindowStateChangeObserver : public aura::WindowObserver {
public:
WindowStateChangeObserver(aura::Window* window,
chromeos::WindowStateType expected_type,
base::OnceCallback<void(bool)> callback)
: expected_type_(expected_type), callback_(std::move(callback)) {
DCHECK_NE(window->GetProperty(chromeos::kWindowStateTypeKey),
expected_type_);
scoped_observation_.Observe(window);
}
WindowStateChangeObserver(const WindowStateChangeObserver&) = delete;
WindowStateChangeObserver& operator=(const WindowStateChangeObserver&) =
delete;
~WindowStateChangeObserver() override {}
// aura::WindowObserver:
void OnWindowPropertyChanged(aura::Window* window,
const void* key,
intptr_t old) override {
DCHECK(scoped_observation_.IsObservingSource(window));
if (key == chromeos::kWindowStateTypeKey &&
window->GetProperty(chromeos::kWindowStateTypeKey) == expected_type_) {
scoped_observation_.Reset();
std::move(callback_).Run(/*success=*/true);
}
}
void OnWindowDestroying(aura::Window* window) override {
DCHECK(scoped_observation_.IsObservingSource(window));
scoped_observation_.Reset();
std::move(callback_).Run(/*success=*/false);
}
private:
chromeos::WindowStateType expected_type_;
base::ScopedObservation<aura::Window, aura::WindowObserver>
scoped_observation_{this};
base::OnceCallback<void(bool)> callback_;
};
class WindowBoundsChangeObserver : public aura::WindowObserver {
public:
WindowBoundsChangeObserver(
aura::Window* window,
const gfx::Rect& to_bounds,
int64_t display_id,
base::OnceCallback<void(const gfx::Rect&, int64_t, bool)> callback)
: callback_(std::move(callback)) {
auto* state = ash::WindowState::Get(window);
DCHECK(state);
wait_for_bounds_change_ = window->GetBoundsInRootWindow() != to_bounds;
wait_for_display_change_ = state->GetDisplay().id() != display_id;
DCHECK(wait_for_bounds_change_ || wait_for_display_change_);
scoped_observation_.Observe(window);
}
~WindowBoundsChangeObserver() override = default;
WindowBoundsChangeObserver(const WindowBoundsChangeObserver&) = delete;
WindowBoundsChangeObserver& operator=(const WindowBoundsChangeObserver&) =
delete;
// aura::WindowObserver:
void OnWindowBoundsChanged(aura::Window* window,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds,
ui::PropertyChangeReason reason) override {
wait_for_bounds_change_ = false;
MaybeFinishObserving(window, /*success=*/true);
}
void OnWindowAddedToRootWindow(aura::Window* window) override {
wait_for_display_change_ = false;
MaybeFinishObserving(window, /*success=*/true);
}
void OnWindowDestroying(aura::Window* window) override {
wait_for_display_change_ = false;
wait_for_bounds_change_ = false;
MaybeFinishObserving(window, /*success=*/false);
}
private:
void MaybeFinishObserving(aura::Window* window, bool success) {
DCHECK(scoped_observation_.IsObservingSource(window));
if (!wait_for_bounds_change_ && !wait_for_display_change_) {
scoped_observation_.Reset();
std::move(callback_).Run(window->GetBoundsInRootWindow(),
ash::WindowState::Get(window)->GetDisplay().id(),
success);
}
}
base::ScopedObservation<aura::Window, aura::WindowObserver>
scoped_observation_{this};
bool wait_for_bounds_change_ = false;
bool wait_for_display_change_ = false;
base::OnceCallback<void(const gfx::Rect&, int64_t, bool)> callback_;
};
class EventGenerator {
public:
EventGenerator(aura::WindowTreeHost* host, base::OnceClosure closure)
: input_injector_(
ui::OzonePlatform::GetInstance()->CreateSystemInputInjector()),
host_(host),
interval_(base::Seconds(1) /
std::max(host->compositor()->refresh_rate(), 60.0f)),
closure_(std::move(closure)),
weak_ptr_factory_(this) {
// VM may report slightly lower than 60hz refresh rate.
LOG_IF(ERROR, host->compositor()->refresh_rate() < 59.98f)
<< "Refresh rate (" << host->compositor()->refresh_rate()
<< ") is too low.";
}
~EventGenerator() = default;
void ScheduleMouseEvent(ui::EventType type,
gfx::PointF location_in_screen,
int flags) {
if (flags == 0 && (type == ui::EventType::kMousePressed ||
type == ui::EventType::kMouseReleased)) {
LOG(ERROR) << "No flags specified for mouse button changes";
}
tasks_.push_back(Task(type, location_in_screen, flags));
}
void Run() {
next_event_timestamp_ = base::TimeTicks::Now();
SendEvent();
}
const base::TimeDelta& interval() const { return interval_; }
private:
struct Task {
enum Status {
kNotScheduled,
kScheduled,
};
const ui::EventType type;
const gfx::PointF location_in_screen;
const int flags;
Status status = kNotScheduled;
Task(ui::EventType type, gfx::PointF location_in_screen, int flags)
: type(type), location_in_screen(location_in_screen), flags(flags) {}
};
void SendEvent() {
if (tasks_.empty()) {
std::move(closure_).Run();
return;
}
Task* task = &tasks_.front();
DCHECK_EQ(task->status, Task::kNotScheduled);
// A task can be processed asynchronously; the next task will be scheduled
// after the control returns to the message pump, assuming that implies the
// processing of the current task has finished.
// WindowEventDispatcherObserver was used but the way it works does not
// support nested loop in window move/resize or drag-n-drop. In such
// cases, the mouse move event triggers the nested loop does not finish
// until the nested loop quits. But this blocks future mouse events. Hence
// the operation does not finish and the nested loop does not quit.
task->status = Task::kScheduled;
switch (task->type) {
case ui::EventType::kMousePressed:
case ui::EventType::kMouseReleased: {
bool pressed = (task->type == ui::EventType::kMousePressed);
if (task->flags & ui::EF_LEFT_MOUSE_BUTTON) {
input_injector_->InjectMouseButton(ui::EF_LEFT_MOUSE_BUTTON, pressed);
}
if (task->flags & ui::EF_MIDDLE_MOUSE_BUTTON) {
input_injector_->InjectMouseButton(ui::EF_MIDDLE_MOUSE_BUTTON,
pressed);
}
if (task->flags & ui::EF_RIGHT_MOUSE_BUTTON) {
input_injector_->InjectMouseButton(ui::EF_RIGHT_MOUSE_BUTTON,
pressed);
}
if (task->flags & ui::EF_BACK_MOUSE_BUTTON) {
input_injector_->InjectMouseButton(ui::EF_BACK_MOUSE_BUTTON, pressed);
}
if (task->flags & ui::EF_FORWARD_MOUSE_BUTTON) {
input_injector_->InjectMouseButton(ui::EF_FORWARD_MOUSE_BUTTON,
pressed);
}
break;
}
case ui::EventType::kMouseMoved: {
display::Display display =
display::Screen::GetScreen()->GetDisplayNearestPoint(
gfx::ToFlooredPoint((task->location_in_screen)));
auto* root_window = ash::Shell::GetRootWindowForDisplayId(display.id());
if (!root_window->GetBoundsInScreen().Contains(
gfx::ToFlooredPoint(task->location_in_screen))) {
// Not in any of the display. Does nothing and schedules a new task.
OnFinishedProcessingEvent();
return;
}
gfx::PointF location_in_host(task->location_in_screen);
wm::ConvertPointFromScreen(root_window, &location_in_host);
ConvertPointToHost(root_window, &location_in_host);
if (root_window->GetHost() != host_) {
// Switching to the new display.
host_ = root_window->GetHost();
host_->MoveCursorToLocationInPixels(
gfx::ToFlooredPoint(location_in_host));
}
// The location should be offset by the origin of the root-window since
// ui::SystemInputInjector expects so.
input_injector_->MoveCursorTo(
location_in_host + host_->GetBoundsInPixels().OffsetFromOrigin());
break;
}
default:
NOTREACHED_IN_MIGRATION();
}
// Post a task after scheduling the event and assumes that when the task
// runs, it implies that the processing of the scheduled event is finished.
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&EventGenerator::OnFinishedProcessingEvent,
weak_ptr_factory_.GetWeakPtr()));
}
void OnFinishedProcessingEvent() {
if (tasks_.empty()) {
return;
}
DCHECK_EQ(tasks_.front().status, Task::kScheduled);
tasks_.pop_front();
const auto& runner = base::SequencedTaskRunner::GetCurrentDefault();
auto closure = base::BindOnce(&EventGenerator::SendEvent,
weak_ptr_factory_.GetWeakPtr());
// Non moving tasks can be done immediately.
if (tasks_.empty() || tasks_.front().type == ui::EventType::kMousePressed ||
tasks_.front().type == ui::EventType::kMouseReleased) {
runner->PostTask(FROM_HERE, std::move(closure));
return;
}
next_event_timestamp_ += interval_;
auto now = base::TimeTicks::Now();
base::TimeDelta interval = next_event_timestamp_ - now;
if (interval <= base::TimeDelta()) {
// Looks like event handling could take too long time -- still generate
// the next event with resetting the interval.
LOG(ERROR) << "The handling of the event spent long time and there is "
<< "no time to delay. The next event is supposed to happen at "
<< next_event_timestamp_ << " but now at " << now << ". "
<< "Posting the next event immediately.";
next_event_timestamp_ = now;
runner->PostTask(FROM_HERE, std::move(closure));
} else {
runner->PostDelayedTask(FROM_HERE, std::move(closure), interval);
}
}
std::unique_ptr<ui::SystemInputInjector> input_injector_;
raw_ptr<aura::WindowTreeHost> host_;
base::TimeTicks next_event_timestamp_;
const base::TimeDelta interval_;
base::OnceClosure closure_;
std::deque<Task> tasks_;
base::WeakPtrFactory<EventGenerator> weak_ptr_factory_;
};
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateInitializeEventsFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateInitializeEventsFunction::
~AutotestPrivateInitializeEventsFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateInitializeEventsFunction::Run() {
// AutotestPrivateAPI is lazily initialized, but needs to be created before
// any of its events can be fired, so we get the instance here and return.
AutotestPrivateAPI::GetFactoryInstance()->Get(browser_context());
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateLogoutFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateLogoutFunction::~AutotestPrivateLogoutFunction() = default;
ExtensionFunction::ResponseAction AutotestPrivateLogoutFunction::Run() {
DVLOG(1) << "AutotestPrivateLogoutFunction";
if (!IsTestMode(browser_context())) {
chrome::AttemptUserExit();
}
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateRestartFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateRestartFunction::~AutotestPrivateRestartFunction() = default;
ExtensionFunction::ResponseAction AutotestPrivateRestartFunction::Run() {
DVLOG(1) << "AutotestPrivateRestartFunction";
if (!IsTestMode(browser_context())) {
chrome::AttemptRestart();
}
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateShutdownFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateShutdownFunction::~AutotestPrivateShutdownFunction() = default;
ExtensionFunction::ResponseAction AutotestPrivateShutdownFunction::Run() {
std::optional<api::autotest_private::Shutdown::Params> params =
api::autotest_private::Shutdown::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateShutdownFunction " << params->force;
if (!IsTestMode(browser_context())) {
chrome::AttemptExit();
}
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateLoginStatusFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateLoginStatusFunction::~AutotestPrivateLoginStatusFunction() =
default;
ExtensionFunction::ResponseAction AutotestPrivateLoginStatusFunction::Run() {
DVLOG(1) << "AutotestPrivateLoginStatusFunction";
base::Value::Dict result;
const user_manager::UserManager* user_manager =
user_manager::UserManager::Get();
// default_screen_locker()->locked() is set when the UI is ready, so this
// tells us both views based lockscreen UI and screenlocker are ready.
const bool is_screen_locked =
!!ash::ScreenLocker::default_screen_locker() &&
ash::ScreenLocker::default_screen_locker()->locked();
if (user_manager) {
result.Set("isLoggedIn", user_manager->IsUserLoggedIn());
result.Set("isOwner", user_manager->IsCurrentUserOwner());
result.Set("isScreenLocked", is_screen_locked);
result.Set("isLockscreenWallpaperAnimating",
is_screen_locked && ash::Shell::Get()
->GetPrimaryRootWindowController()
->wallpaper_widget_controller()
->IsAnimating());
result.Set("isReadyForPassword",
ash::LoginScreen::Get()->IsReadyForPassword());
const user_manager::UserList& users = user_manager->GetUsers();
bool user_images_loaded = true;
for (const user_manager::User* user : users) {
if (user->image_is_loading()) {
user_images_loaded = false;
break;
}
}
result.Set("areAllUserImagesLoaded", user_images_loaded);
if (user_manager->IsUserLoggedIn()) {
result.Set("isRegularUser",
user_manager->IsLoggedInAsUserWithGaiaAccount());
result.Set("isGuest", user_manager->IsLoggedInAsGuest());
result.Set("isKiosk", user_manager->IsLoggedInAsKioskApp());
const user_manager::User* user = user_manager->GetActiveUser();
result.Set("email", user->GetAccountId().GetUserEmail());
result.Set("displayEmail", user->display_email());
result.Set("displayName", user->display_name());
std::string user_image;
switch (user->image_index()) {
case user_manager::UserImage::Type::kExternal:
user_image = "file";
break;
case user_manager::UserImage::Type::kProfile:
user_image = "profile";
break;
default:
user_image = base::NumberToString(user->image_index());
break;
}
result.Set("userImage", user_image);
if (user->HasGaiaAccount()) {
result.Set("hasValidOauth2Token",
user->oauth_token_status() ==
user_manager::User::OAUTH2_TOKEN_STATUS_VALID);
}
}
}
return RespondNow(WithArguments(std::move(result)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateWaitForLoginAnimationEndFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateWaitForLoginAnimationEndFunction::
~AutotestPrivateWaitForLoginAnimationEndFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateWaitForLoginAnimationEndFunction::Run() {
DVLOG(1) << "AutotestPrivateWaitForLoginAnimationEndFunction";
ash::Shell::Get()
->login_unlock_throughput_recorder()
->post_login_deferred_task_runner()
->PostTask(
FROM_HERE,
base::BindOnce(&AutotestPrivateWaitForLoginAnimationEndFunction::
OnLoginAnimationEnd,
this));
return RespondLater();
}
void AutotestPrivateWaitForLoginAnimationEndFunction::OnLoginAnimationEnd() {
DVLOG(1)
<< "AutotestPrivateWaitForLoginAnimationEndFunction::OnLoginAnimationEnd";
Respond(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateLockScreenFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateLockScreenFunction::~AutotestPrivateLockScreenFunction() =
default;
ExtensionFunction::ResponseAction AutotestPrivateLockScreenFunction::Run() {
DVLOG(1) << "AutotestPrivateLockScreenFunction";
ash::SessionManagerClient::Get()->RequestLockScreen();
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetAllEnterprisePoliciesFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetAllEnterprisePoliciesFunction::
~AutotestPrivateGetAllEnterprisePoliciesFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateGetAllEnterprisePoliciesFunction::Run() {
DVLOG(1) << "AutotestPrivateGetAllEnterprisePoliciesFunction";
base::Value::Dict all_policies_dict =
policy::PolicyConversions(
std::make_unique<policy::ChromePolicyConversionsClient>(
browser_context()))
.EnableDeviceLocalAccountPolicies(true)
.EnableDeviceInfo(true)
.ToValueDict();
return RespondNow(WithArguments(std::move(all_policies_dict)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateRefreshEnterprisePoliciesFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateRefreshEnterprisePoliciesFunction::
~AutotestPrivateRefreshEnterprisePoliciesFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateRefreshEnterprisePoliciesFunction::Run() {
DVLOG(1) << "AutotestPrivateRefreshEnterprisePoliciesFunction";
g_browser_process->policy_service()->RefreshPolicies(
base::BindOnce(
&AutotestPrivateRefreshEnterprisePoliciesFunction::RefreshDone, this),
policy::PolicyFetchReason::kTest);
return RespondLater();
}
void AutotestPrivateRefreshEnterprisePoliciesFunction::RefreshDone() {
Respond(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateRefreshRemoteCommandsFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateRefreshRemoteCommandsFunction::
~AutotestPrivateRefreshRemoteCommandsFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateRefreshRemoteCommandsFunction::Run() {
DVLOG(1) << "AutotestPrivateRefreshRemoteCommandsFunction";
// Allow tests to manually fetch remote commands. Useful for testing or when
// the invalidation service is not working properly.
policy::CloudPolicyManager* const device_manager =
g_browser_process->platform_part()
->browser_policy_connector_ash()
->GetDeviceCloudPolicyManager();
policy::CloudPolicyManager* const user_manager =
Profile::FromBrowserContext(browser_context())
->GetUserCloudPolicyManagerAsh();
// Fetch both device and user remote commands.
for (policy::CloudPolicyManager* manager : {device_manager, user_manager}) {
if (manager) {
policy::RemoteCommandsService* const remote_commands_service =
manager->core()->remote_commands_service();
if (remote_commands_service) {
remote_commands_service->FetchRemoteCommands();
}
}
}
// TODO(b/260972611): Wait till remote commands are fetched.
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetExtensionsInfoFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetExtensionsInfoFunction::
~AutotestPrivateGetExtensionsInfoFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateGetExtensionsInfoFunction::Run() {
DVLOG(1) << "AutotestPrivateGetExtensionsInfoFunction";
ExtensionService* service =
ExtensionSystem::Get(browser_context())->extension_service();
ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context());
const ExtensionSet& extensions = registry->enabled_extensions();
const ExtensionSet& disabled_extensions = registry->disabled_extensions();
ExtensionActionManager* extension_action_manager =
ExtensionActionManager::Get(browser_context());
base::Value::List extensions_values;
ExtensionList all;
all.insert(all.end(), extensions.begin(), extensions.end());
all.insert(all.end(), disabled_extensions.begin(), disabled_extensions.end());
for (ExtensionList::const_iterator it = all.begin(); it != all.end(); ++it) {
const Extension* extension = it->get();
std::string id = extension->id();
ManifestLocation location = extension->location();
const ExtensionAction* action =
extension_action_manager->GetExtensionAction(*extension);
extensions_values.Append(
base::Value::Dict()
.Set("id", id)
.Set("version", extension->VersionString())
.Set("name", extension->name())
.Set("publicKey", extension->public_key())
.Set("description", extension->description())
.Set("backgroundUrl",
BackgroundInfo::GetBackgroundURL(extension).spec())
.Set("optionsUrl",
OptionsPageInfo::GetOptionsPage(extension).spec())
.Set("hostPermissions", GetHostPermissions(extension, false))
.Set("effectiveHostPermissions",
GetHostPermissions(extension, true))
.Set("apiPermissions", GetAPIPermissions(extension))
.Set("isComponent", location == ManifestLocation::kComponent)
.Set("isInternal", location == ManifestLocation::kInternal)
.Set("isUserInstalled", location == ManifestLocation::kInternal ||
Manifest::IsUnpackedLocation(location))
.Set("isEnabled", service->IsExtensionEnabled(id))
.Set("allowedInIncognito",
util::IsIncognitoEnabled(id, browser_context()))
.Set("hasPageAction",
action && action->action_type() == ActionInfo::Type::kPage));
}
return RespondNow(WithArguments(
base::Value::Dict().Set("extensions", std::move(extensions_values))));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSimulateAsanMemoryBugFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSimulateAsanMemoryBugFunction::
~AutotestPrivateSimulateAsanMemoryBugFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSimulateAsanMemoryBugFunction::Run() {
DVLOG(1) << "AutotestPrivateSimulateAsanMemoryBugFunction";
if (!IsTestMode(browser_context())) {
int testarray[3] = {0, 0, 0};
// Cause Address Sanitizer to abort this process.
int index = 5;
AccessArray(testarray, &index);
}
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetTouchpadSensitivityFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetTouchpadSensitivityFunction::
~AutotestPrivateSetTouchpadSensitivityFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetTouchpadSensitivityFunction::Run() {
std::optional<api::autotest_private::SetTouchpadSensitivity::Params> params =
api::autotest_private::SetTouchpadSensitivity::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateSetTouchpadSensitivityFunction " << params->value;
ash::system::InputDeviceSettings::Get()->SetTouchpadSensitivity(
params->value);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetTapToClickFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetTapToClickFunction::~AutotestPrivateSetTapToClickFunction() =
default;
ExtensionFunction::ResponseAction AutotestPrivateSetTapToClickFunction::Run() {
std::optional<api::autotest_private::SetTapToClick::Params> params =
api::autotest_private::SetTapToClick::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateSetTapToClickFunction " << params->enabled;
ash::system::InputDeviceSettings::Get()->SetTapToClick(params->enabled);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetThreeFingerClickFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetThreeFingerClickFunction::
~AutotestPrivateSetThreeFingerClickFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetThreeFingerClickFunction::Run() {
std::optional<api::autotest_private::SetThreeFingerClick::Params> params =
api::autotest_private::SetThreeFingerClick::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateSetThreeFingerClickFunction " << params->enabled;
ash::system::InputDeviceSettings::Get()->SetThreeFingerClick(params->enabled);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetTapDraggingFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetTapDraggingFunction::
~AutotestPrivateSetTapDraggingFunction() = default;
ExtensionFunction::ResponseAction AutotestPrivateSetTapDraggingFunction::Run() {
std::optional<api::autotest_private::SetTapDragging::Params> params =
api::autotest_private::SetTapDragging::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateSetTapDraggingFunction " << params->enabled;
ash::system::InputDeviceSettings::Get()->SetTapDragging(params->enabled);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetNaturalScrollFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetNaturalScrollFunction::
~AutotestPrivateSetNaturalScrollFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetNaturalScrollFunction::Run() {
std::optional<api::autotest_private::SetNaturalScroll::Params> params =
api::autotest_private::SetNaturalScroll::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateSetNaturalScrollFunction " << params->enabled;
ash::system::InputDeviceSettings::Get()->SetNaturalScroll(params->enabled);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetMouseSensitivityFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetMouseSensitivityFunction::
~AutotestPrivateSetMouseSensitivityFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetMouseSensitivityFunction::Run() {
std::optional<api::autotest_private::SetMouseSensitivity::Params> params =
api::autotest_private::SetMouseSensitivity::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateSetMouseSensitivityFunction " << params->value;
ash::system::InputDeviceSettings::Get()->SetMouseSensitivity(params->value);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetPrimaryButtonRightFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetPrimaryButtonRightFunction::
~AutotestPrivateSetPrimaryButtonRightFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetPrimaryButtonRightFunction::Run() {
std::optional<api::autotest_private::SetPrimaryButtonRight::Params> params =
api::autotest_private::SetPrimaryButtonRight::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateSetPrimaryButtonRightFunction " << params->right;
ash::system::InputDeviceSettings::Get()->SetPrimaryButtonRight(params->right);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetMouseReverseScrollFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetMouseReverseScrollFunction::
~AutotestPrivateSetMouseReverseScrollFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetMouseReverseScrollFunction::Run() {
std::optional<api::autotest_private::SetMouseReverseScroll::Params> params =
api::autotest_private::SetMouseReverseScroll::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateSetMouseReverseScrollFunction "
<< params->enabled;
ash::system::InputDeviceSettings::Get()->SetMouseReverseScroll(
params->enabled);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetVisibleNotificationsFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetVisibleNotificationsFunction::
AutotestPrivateGetVisibleNotificationsFunction() = default;
AutotestPrivateGetVisibleNotificationsFunction::
~AutotestPrivateGetVisibleNotificationsFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateGetVisibleNotificationsFunction::Run() {
DVLOG(1) << "AutotestPrivateGetVisibleNotificationsFunction";
message_center::NotificationList::Notifications notification_set =
message_center::MessageCenter::Get()->GetVisibleNotifications();
base::Value::List values;
for (message_center::Notification* notification : notification_set) {
values.Append(MakeDictionaryFromNotification(*notification));
}
return RespondNow(WithArguments(std::move(values)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateRemoveAllNotificationsFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateRemoveAllNotificationsFunction::
AutotestPrivateRemoveAllNotificationsFunction() = default;
AutotestPrivateRemoveAllNotificationsFunction::
~AutotestPrivateRemoveAllNotificationsFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateRemoveAllNotificationsFunction::Run() {
DVLOG(1) << "AutotestPrivateRemoveAllNotificationsFunction";
message_center::MessageCenter::Get()->RemoveAllNotifications(
/*by_user=*/false, message_center::MessageCenter::RemoveType::ALL);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetArcStartTimeFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetArcStartTimeFunction::
~AutotestPrivateGetArcStartTimeFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateGetArcStartTimeFunction::Run() {
DVLOG(1) << "AutotestPrivateGetArcStartTimeFunction";
arc::ArcSessionManager* arc_session_manager = arc::ArcSessionManager::Get();
if (!arc_session_manager) {
return RespondNow(Error("Could not find ARC session manager"));
}
const double start_ticks =
(arc_session_manager->start_time() - base::TimeTicks()).InMillisecondsF();
return RespondNow(WithArguments(start_ticks));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetArcStateFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetArcStateFunction::~AutotestPrivateGetArcStateFunction() =
default;
ExtensionFunction::ResponseAction AutotestPrivateGetArcStateFunction::Run() {
DVLOG(1) << "AutotestPrivateGetArcStateFunction";
api::autotest_private::ArcState arc_state;
Profile* const profile = Profile::FromBrowserContext(browser_context());
if (!arc::IsArcAllowedForProfile(profile)) {
return RespondNow(Error("ARC is not available for the current user"));
}
arc::ArcSessionManager* const arc_session_manager =
arc::ArcSessionManager::Get();
if (!arc_session_manager) {
return RespondNow(Error("Could not find ARC session manager"));
}
const base::Time now_time = base::Time::Now();
const base::TimeTicks now_ticks = base::TimeTicks::Now();
const base::TimeTicks pre_start_time = arc_session_manager->pre_start_time();
const base::TimeTicks start_time = arc_session_manager->start_time();
arc_state.provisioned = arc::IsArcProvisioned(profile);
arc_state.tos_needed = arc::IsArcTermsOfServiceNegotiationNeeded(profile);
arc_state.pre_start_time = pre_start_time.is_null()
? 0
: (now_time - (now_ticks - pre_start_time))
.InMillisecondsFSinceUnixEpoch();
arc_state.start_time = start_time.is_null()
? 0
: (now_time - (now_ticks - start_time))
.InMillisecondsFSinceUnixEpoch();
return RespondNow(WithArguments(arc_state.ToValue()));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetPlayStoreStateFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetPlayStoreStateFunction::
~AutotestPrivateGetPlayStoreStateFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateGetPlayStoreStateFunction::Run() {
DVLOG(1) << "AutotestPrivateGetPlayStoreStateFunction";
api::autotest_private::PlayStoreState play_store_state;
play_store_state.allowed = false;
Profile* profile = Profile::FromBrowserContext(browser_context());
if (arc::IsArcAllowedForProfile(profile)) {
play_store_state.allowed = true;
play_store_state.enabled = arc::IsArcPlayStoreEnabledForProfile(profile);
play_store_state.managed =
arc::IsArcPlayStoreEnabledPreferenceManagedForProfile(profile);
}
return RespondNow(WithArguments(play_store_state.ToValue()));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateStartArcFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateStartArcFunction::~AutotestPrivateStartArcFunction() = default;
ExtensionFunction::ResponseAction AutotestPrivateStartArcFunction::Run() {
DVLOG(1) << "AutotestPrivateStartArcFunction";
arc::ArcSessionManager* arc_session_manager = arc::ArcSessionManager::Get();
if (!arc_session_manager) {
return RespondNow(Error("Could not find ARC session manager"));
}
Profile* profile = Profile::FromBrowserContext(browser_context());
if (!arc::IsArcAllowedForProfile(profile)) {
return RespondNow(Error("ARC cannot be started for the current user"));
}
if (arc_session_manager->enable_requested()) {
return RespondNow(Error("ARC is already started"));
}
arc_session_manager->RequestEnable();
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateStopArcFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateStopArcFunction::~AutotestPrivateStopArcFunction() = default;
ExtensionFunction::ResponseAction AutotestPrivateStopArcFunction::Run() {
DVLOG(1) << "AutotestPrivateStopArcFunction";
arc::ArcSessionManager* arc_session_manager = arc::ArcSessionManager::Get();
if (!arc_session_manager) {
return RespondNow(Error("Could not find ARC session manager"));
}
if (!arc_session_manager->enable_requested()) {
return RespondNow(Error("ARC is already stopped"));
}
arc_session_manager->RequestDisable();
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetPlayStoreEnabledFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetPlayStoreEnabledFunction::
~AutotestPrivateSetPlayStoreEnabledFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetPlayStoreEnabledFunction::Run() {
std::optional<api::autotest_private::SetPlayStoreEnabled::Params> params =
api::autotest_private::SetPlayStoreEnabled::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateSetPlayStoreEnabledFunction " << params->enabled;
Profile* profile = Profile::FromBrowserContext(browser_context());
if (arc::IsArcAllowedForProfile(profile)) {
if (!arc::SetArcPlayStoreEnabledForProfile(profile, params->enabled)) {
return RespondNow(
Error("ARC enabled state cannot be changed for the current user"));
}
// kArcLocationServiceEnabled and kArcBackupRestoreEnabled are prefs that
// set together with enabling ARC. That is why we set it here not using
// SetAllowedPref. At this moment, we don't distinguish the actual
// values and set kArcLocationServiceEnabled to true and leave
// kArcBackupRestoreEnabled unmodified, which is acceptable for autotests
// currently.
profile->GetPrefs()->SetBoolean(arc::prefs::kArcLocationServiceEnabled,
true);
// Since we are settings location to enabled, we don't have to sync this
// settings from android.
if (base::FeatureList::IsEnabled(ash::features::kCrosPrivacyHub)) {
profile->GetPrefs()->SetBoolean(
arc::prefs::kArcInitialLocationSettingSyncRequired, false);
}
return RespondNow(NoArguments());
} else {
return RespondNow(Error("ARC is not available for the current user"));
}
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateIsAppShownFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateIsAppShownFunction::~AutotestPrivateIsAppShownFunction() =
default;
ExtensionFunction::ResponseAction AutotestPrivateIsAppShownFunction::Run() {
std::optional<api::autotest_private::IsAppShown::Params> params =
api::autotest_private::IsAppShown::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateIsAppShownFunction " << params->app_id;
ChromeShelfController* const controller = ChromeShelfController::instance();
if (!controller) {
return RespondNow(Error("Controller not available"));
}
const ash::ShelfItem* item =
controller->GetItem(ash::ShelfID(params->app_id));
// App must be running and not pending in deferred launch.
const bool window_attached =
item && item->status == ash::ShelfItemStatus::STATUS_RUNNING &&
!controller->GetShelfSpinnerController()->HasApp(params->app_id);
return RespondNow(WithArguments(window_attached));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateIsAppShownFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateIsArcProvisionedFunction::
~AutotestPrivateIsArcProvisionedFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateIsArcProvisionedFunction::Run() {
DVLOG(1) << "AutotestPrivateIsArcProvisionedFunction";
return RespondNow(WithArguments(
arc::IsArcProvisioned(Profile::FromBrowserContext(browser_context()))));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetLacrosInfoFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetLacrosInfoFunction::~AutotestPrivateGetLacrosInfoFunction() =
default;
// static
api::autotest_private::LacrosState
AutotestPrivateGetLacrosInfoFunction::ToLacrosState(
crosapi::BrowserManager::State state) {
switch (state) {
case crosapi::BrowserManager::State::NOT_INITIALIZED:
return api::autotest_private::LacrosState::kNotInitialized;
case crosapi::BrowserManager::State::RELOADING:
return api::autotest_private::LacrosState::kReloading;
case crosapi::BrowserManager::State::MOUNTING:
return api::autotest_private::LacrosState::kMounting;
case crosapi::BrowserManager::State::UNAVAILABLE:
return api::autotest_private::LacrosState::kUnavailable;
case crosapi::BrowserManager::State::STOPPED:
return api::autotest_private::LacrosState::kStopped;
case crosapi::BrowserManager::State::PREPARING_FOR_LAUNCH:
return api::autotest_private::LacrosState::kPreparingForLaunch;
case crosapi::BrowserManager::State::PRE_LAUNCHED:
return api::autotest_private::LacrosState::kPreLaunched;
case crosapi::BrowserManager::State::STARTING:
return api::autotest_private::LacrosState::kStarting;
case crosapi::BrowserManager::State::RUNNING:
return api::autotest_private::LacrosState::kRunning;
case crosapi::BrowserManager::State::WAITING_FOR_MOJO_DISCONNECTED:
return api::autotest_private::LacrosState::kWaitingForMojoDisconnected;
case crosapi::BrowserManager::State::WAITING_FOR_PROCESS_TERMINATED:
return api::autotest_private::LacrosState::kWaitingForProcessTerminated;
}
}
// static
api::autotest_private::LacrosMode
AutotestPrivateGetLacrosInfoFunction::ToLacrosMode(bool is_enabled) {
return is_enabled ? api::autotest_private::LacrosMode::kOnly
: api::autotest_private::LacrosMode::kDisabled;
}
ExtensionFunction::ResponseAction AutotestPrivateGetLacrosInfoFunction::Run() {
DVLOG(1) << "AutotestPrivateGetLacrosInfoFunction";
auto* browser_manager = crosapi::BrowserManager::Get();
return RespondNow(WithArguments(
base::Value::Dict()
.Set("state", api::autotest_private::ToString(
ToLacrosState(browser_manager->state_)))
.Set("isKeepAlive", browser_manager->IsKeepAliveEnabled())
// TODO(neis): Rename lacrosPath to avoid confusion, or make it be the
// binary path. Either requires changes in tast-tests.
.Set("lacrosPath",
browser_manager->lacros_path().empty()
? ""
: browser_manager->lacros_path().DirName().MaybeAsASCII())
.Set("mode", api::autotest_private::ToString(ToLacrosMode(
crosapi::browser_util::IsLacrosEnabled())))
.Set("isEnabled", crosapi::browser_util::IsLacrosEnabled())));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetArcAppFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetArcAppFunction::~AutotestPrivateGetArcAppFunction() = default;
ExtensionFunction::ResponseAction AutotestPrivateGetArcAppFunction::Run() {
std::optional<api::autotest_private::GetArcApp::Params> params =
api::autotest_private::GetArcApp::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateGetArcAppFunction " << params->app_id;
ArcAppListPrefs* const prefs =
ArcAppListPrefs::Get(Profile::FromBrowserContext(browser_context()));
if (!prefs) {
return RespondNow(Error("ARC is not available"));
}
const std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
prefs->GetApp(params->app_id);
if (!app_info) {
return RespondNow(Error("App is not available"));
}
return RespondNow(WithArguments(
base::Value::Dict()
.Set("name", std::move(app_info->name))
.Set("packageName", std::move(app_info->package_name))
.Set("activity", std::move(app_info->activity))
.Set("intentUri", std::move(app_info->intent_uri))
.Set("iconResourceId", std::move(app_info->icon_resource_id))
.Set("lastLaunchTime",
app_info->last_launch_time.InMillisecondsFSinceUnixEpoch())
.Set("installTime",
app_info->install_time.InMillisecondsFSinceUnixEpoch())
.Set("sticky", app_info->sticky)
.Set("notificationsEnabled", app_info->notifications_enabled)
.Set("ready", app_info->ready)
.Set("suspended", app_info->suspended)
.Set("showInLauncher", app_info->show_in_launcher)
.Set("shortcut", app_info->shortcut)
.Set("launchable", app_info->launchable)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetArcAppKillsFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetArcAppKillsFunction::
~AutotestPrivateGetArcAppKillsFunction() = default;
ExtensionFunction::ResponseAction AutotestPrivateGetArcAppKillsFunction::Run() {
DVLOG(1) << "AutotestPrivateGetArcAppKillsFunction";
arc::ArcServiceManager* arc_service_manager = arc::ArcServiceManager::Get();
if (!arc_service_manager) {
return RespondNow(Error("ARC service manager is not available"));
}
arc::ArcBridgeService* arc_bridge_service =
arc_service_manager->arc_bridge_service();
if (!arc_bridge_service) {
return RespondNow(Error("ARC bridge service is not available"));
}
arc::mojom::ProcessInstance* process_instance = ARC_GET_INSTANCE_FOR_METHOD(
arc_bridge_service->process(), RequestLowMemoryKillCounts);
if (!process_instance) {
return RespondNow(Error("ARC process service is not available"));
}
process_instance->RequestLowMemoryKillCounts(base::BindOnce(
&AutotestPrivateGetArcAppKillsFunction::OnKillCounts, this));
return RespondLater();
}
void AutotestPrivateGetArcAppKillsFunction::OnKillCounts(
arc::mojom::LowMemoryKillCountsPtr counts) {
api::autotest_private::ArcAppKillsDict result;
result.oom = counts->guest_oom;
result.lmkd_foreground = counts->lmkd_foreground;
result.lmkd_perceptible = counts->lmkd_perceptible;
result.lmkd_cached = counts->lmkd_cached;
result.pressure_foreground = counts->pressure_foreground;
result.pressure_perceptible = counts->pressure_perceptible;
result.pressure_cached = counts->pressure_cached;
Respond(WithArguments(result.ToValue()));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetArcPackageFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetArcPackageFunction::~AutotestPrivateGetArcPackageFunction() =
default;
ExtensionFunction::ResponseAction AutotestPrivateGetArcPackageFunction::Run() {
std::optional<api::autotest_private::GetArcPackage::Params> params =
api::autotest_private::GetArcPackage::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateGetArcPackageFunction " << params->package_name;
ArcAppListPrefs* const prefs =
ArcAppListPrefs::Get(Profile::FromBrowserContext(browser_context()));
if (!prefs) {
return RespondNow(Error("ARC is not available"));
}
base::Value::Dict package_value;
{
const std::unique_ptr<ArcAppListPrefs::PackageInfo> package_info =
prefs->GetPackage(params->package_name);
if (!package_info) {
return RespondNow(Error("Package is not available"));
}
package_value.Set("packageName", std::move(package_info->package_name));
package_value.Set("packageVersion", package_info->package_version);
package_value.Set(
"lastBackupAndroidId",
base::NumberToString(package_info->last_backup_android_id));
package_value.Set("lastBackupTime",
base::Time::FromDeltaSinceWindowsEpoch(
base::Microseconds(package_info->last_backup_time))
.InMillisecondsFSinceUnixEpoch());
package_value.Set("shouldSync", package_info->should_sync);
package_value.Set("vpnProvider", package_info->vpn_provider);
}
return RespondNow(WithArguments(std::move(package_value)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateWaitForSystemWebAppsInstallFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateWaitForSystemWebAppsInstallFunction::
AutotestPrivateWaitForSystemWebAppsInstallFunction() = default;
AutotestPrivateWaitForSystemWebAppsInstallFunction::
~AutotestPrivateWaitForSystemWebAppsInstallFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateWaitForSystemWebAppsInstallFunction::Run() {
Profile* profile = Profile::FromBrowserContext(browser_context());
ash::SystemWebAppManager* swa_manager =
ash::SystemWebAppManager::Get(profile);
if (!swa_manager) {
return RespondNow(Error("System Web Apps are not available for profile."));
}
swa_manager->on_apps_synchronized().Post(
FROM_HERE,
base::BindOnce(
&AutotestPrivateWaitForSystemWebAppsInstallFunction::Respond, this,
NoArguments()));
return RespondLater();
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetRegisteredSystemWebAppsFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetRegisteredSystemWebAppsFunction::
AutotestPrivateGetRegisteredSystemWebAppsFunction() = default;
AutotestPrivateGetRegisteredSystemWebAppsFunction::
~AutotestPrivateGetRegisteredSystemWebAppsFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateGetRegisteredSystemWebAppsFunction::Run() {
Profile* profile = Profile::FromBrowserContext(browser_context());
ash::SystemWebAppManager* swa_manager =
ash::SystemWebAppManager::Get(profile);
if (!swa_manager) {
return RespondNow(Error("System Web Apps are not available for profile."));
}
swa_manager->on_apps_synchronized().Post(
FROM_HERE,
base::BindOnce(&AutotestPrivateGetRegisteredSystemWebAppsFunction::
OnSystemWebAppsInstalled,
this));
return RespondLater();
}
void AutotestPrivateGetRegisteredSystemWebAppsFunction::
OnSystemWebAppsInstalled() {
Profile* profile = Profile::FromBrowserContext(browser_context());
ash::SystemWebAppManager* swa_manager =
ash::SystemWebAppManager::Get(profile);
std::vector<api::autotest_private::SystemWebApp> result;
for (const auto& type_and_info : swa_manager->system_app_delegates()) {
api::autotest_private::SystemWebApp system_web_app;
ash::SystemWebAppDelegate* delegate = type_and_info.second.get();
system_web_app.internal_name = delegate->GetInternalName();
system_web_app.url =
delegate->GetInstallUrl().DeprecatedGetOriginAsURL().spec();
system_web_app.name = base::UTF16ToUTF8(delegate->GetWebAppInfo()->title);
std::optional<webapps::AppId> app_id =
swa_manager->GetAppIdForSystemApp(type_and_info.first);
if (app_id) {
system_web_app.start_url =
ash::SystemWebAppManager::GetWebAppProvider(profile)
->registrar_unsafe()
.GetAppLaunchUrl(*app_id)
.spec();
}
result.push_back(std::move(system_web_app));
}
Respond(ArgumentList(
api::autotest_private::GetRegisteredSystemWebApps::Results::Create(
result)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateIsSystemWebAppOpenFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateIsSystemWebAppOpenFunction::
AutotestPrivateIsSystemWebAppOpenFunction() = default;
AutotestPrivateIsSystemWebAppOpenFunction::
~AutotestPrivateIsSystemWebAppOpenFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateIsSystemWebAppOpenFunction::Run() {
Profile* profile = Profile::FromBrowserContext(browser_context());
ash::SystemWebAppManager* swa_manager =
ash::SystemWebAppManager::Get(profile);
if (!swa_manager) {
return RespondNow(Error("System web Apps are not available for profile."));
}
std::optional<api::autotest_private::IsSystemWebAppOpen::Params> params =
api::autotest_private::IsSystemWebAppOpen::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateIsSystemWebAppOpenFunction " << params->app_id;
swa_manager->on_apps_synchronized().Post(
FROM_HERE,
base::BindOnce(
&AutotestPrivateIsSystemWebAppOpenFunction::OnSystemWebAppsInstalled,
this));
return RespondLater();
}
void AutotestPrivateIsSystemWebAppOpenFunction::OnSystemWebAppsInstalled() {
Profile* profile = Profile::FromBrowserContext(browser_context());
std::optional<api::autotest_private::IsSystemWebAppOpen::Params> params =
api::autotest_private::IsSystemWebAppOpen::Params::Create(args());
std::optional<ash::SystemWebAppType> app_type =
ash::GetSystemWebAppTypeForAppId(profile, params->app_id);
if (!app_type) {
Respond(Error("No system web app is found by given app id."));
return;
}
Respond(WithArguments(ash::FindSystemWebAppBrowser(profile, *app_type) !=
nullptr));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateLaunchAppFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateLaunchAppFunction::~AutotestPrivateLaunchAppFunction() = default;
ExtensionFunction::ResponseAction AutotestPrivateLaunchAppFunction::Run() {
std::optional<api::autotest_private::LaunchApp::Params> params =
api::autotest_private::LaunchApp::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateLaunchAppFunction " << params->app_id;
ChromeShelfController* const controller = ChromeShelfController::instance();
if (!controller) {
return RespondNow(Error("Controller not available"));
}
controller->LaunchApp(ash::ShelfID(params->app_id),
ash::ShelfLaunchSource::LAUNCH_FROM_INTERNAL,
0, /* event_flags */
display::Screen::GetScreen()->GetPrimaryDisplay().id());
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateLaunchSystemWebAppFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateLaunchSystemWebAppFunction::
~AutotestPrivateLaunchSystemWebAppFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateLaunchSystemWebAppFunction::Run() {
std::optional<api::autotest_private::LaunchSystemWebApp::Params> params =
api::autotest_private::LaunchSystemWebApp::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateLaunchSystemWebAppFunction name: "
<< params->app_name << " url: " << params->url;
Profile* profile = Profile::FromBrowserContext(browser_context());
ash::SystemWebAppManager* swa_manager =
ash::SystemWebAppManager::Get(profile);
if (!swa_manager) {
return RespondNow(Error("System Web Apps are not available for profile."));
}
swa_manager->on_apps_synchronized().Post(
FROM_HERE,
base::BindOnce(
&AutotestPrivateLaunchSystemWebAppFunction::OnSystemWebAppsInstalled,
this));
return RespondLater();
}
void AutotestPrivateLaunchSystemWebAppFunction::OnSystemWebAppsInstalled() {
Profile* profile = Profile::FromBrowserContext(browser_context());
ash::SystemWebAppManager* swa_manager =
ash::SystemWebAppManager::Get(profile);
std::optional<api::autotest_private::LaunchSystemWebApp::Params> params =
api::autotest_private::LaunchSystemWebApp::Params::Create(args());
std::optional<ash::SystemWebAppType> app_type;
for (const auto& type_and_info : swa_manager->system_app_delegates()) {
if (type_and_info.second->GetInternalName() == params->app_name) {
app_type = type_and_info.first;
break;
}
}
if (!app_type.has_value()) {
Respond(Error("No mapped system web app found"));
return;
}
ash::SystemAppLaunchParams swa_params;
swa_params.url = GURL(params->url);
ash::LaunchSystemWebAppAsync(profile, *app_type, swa_params);
Respond(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateLaunchFilesAppToPathFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateLaunchFilesAppToPathFunction::
~AutotestPrivateLaunchFilesAppToPathFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateLaunchFilesAppToPathFunction::Run() {
std::optional<api::autotest_private::LaunchFilesAppToPath::Params> params =
api::autotest_private::LaunchFilesAppToPath::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
base::FilePath absolute_path(params->absolute_path);
if (!absolute_path.IsAbsolute()) {
return RespondNow(Error("Supplied path is not absolute"));
}
Profile* profile = Profile::FromBrowserContext(browser_context());
file_manager::util::ShowItemInFolder(
profile, std::move(absolute_path),
base::BindOnce(
&AutotestPrivateLaunchFilesAppToPathFunction::OnShowItemInFolder,
this));
return RespondLater();
}
void AutotestPrivateLaunchFilesAppToPathFunction::OnShowItemInFolder(
platform_util::OpenOperationResult result) {
if (result != platform_util::OpenOperationResult::OPEN_SUCCEEDED) {
DVLOG(1) << "Failed navigating to folder with error: " << result;
Respond(Error("Failed trying to open the supplied path"));
return;
}
Respond(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateCloseAppFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateCloseAppFunction::~AutotestPrivateCloseAppFunction() = default;
ExtensionFunction::ResponseAction AutotestPrivateCloseAppFunction::Run() {
std::optional<api::autotest_private::CloseApp::Params> params =
api::autotest_private::CloseApp::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateCloseAppFunction " << params->app_id;
ChromeShelfController* const controller = ChromeShelfController::instance();
if (!controller) {
return RespondNow(Error("Controller not available"));
}
controller->Close(ash::ShelfID(params->app_id));
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetClipboardTextDataFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetClipboardTextDataFunction::
~AutotestPrivateGetClipboardTextDataFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateGetClipboardTextDataFunction::Run() {
std::u16string data;
// This clipboard data read is initiated an extension API, then the user
// shouldn't see a notification if the clipboard is restricted by the rules of
// data leak prevention policy.
ui::DataTransferEndpoint data_dst = ui::DataTransferEndpoint(
ui::EndpointType::kDefault, {.notify_if_restricted = false});
ui::Clipboard::GetForCurrentThread()->ReadText(
ui::ClipboardBuffer::kCopyPaste, &data_dst, &data);
return RespondNow(WithArguments(data));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetClipboardTextDataFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetClipboardTextDataFunction::
AutotestPrivateSetClipboardTextDataFunction() = default;
AutotestPrivateSetClipboardTextDataFunction::
~AutotestPrivateSetClipboardTextDataFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetClipboardTextDataFunction::Run() {
observation_.Observe(ui::ClipboardMonitor::GetInstance());
std::optional<api::autotest_private::SetClipboardTextData::Params> params =
api::autotest_private::SetClipboardTextData::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
const std::u16string data = base::UTF8ToUTF16(params->data);
ui::ScopedClipboardWriter clipboard_writer(ui::ClipboardBuffer::kCopyPaste);
clipboard_writer.WriteText(data);
return did_respond() ? AlreadyResponded() : RespondLater();
}
void AutotestPrivateSetClipboardTextDataFunction::OnClipboardDataChanged() {
observation_.Reset();
Respond(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetCrostiniEnabledFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetCrostiniEnabledFunction::
~AutotestPrivateSetCrostiniEnabledFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetCrostiniEnabledFunction::Run() {
std::optional<api::autotest_private::SetCrostiniEnabled::Params> params =
api::autotest_private::SetCrostiniEnabled::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateSetCrostiniEnabledFunction " << params->enabled;
Profile* profile = Profile::FromBrowserContext(browser_context());
if (!crostini::CrostiniFeatures::Get()->IsAllowedNow(profile)) {
return RespondNow(Error(kCrostiniNotAvailableForCurrentUserError));
}
// Set the preference to indicate Crostini is enabled/disabled.
profile->GetPrefs()->SetBoolean(crostini::prefs::kCrostiniEnabled,
params->enabled);
// Set the flag to indicate we are in testing mode so that Chrome doesn't
// try to start the VM/container itself.
crostini::CrostiniManager* crostini_manager =
crostini::CrostiniManager::GetForProfile(profile);
crostini_manager->set_skip_restart_for_testing();
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateRunCrostiniInstallerFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateRunCrostiniInstallerFunction::
~AutotestPrivateRunCrostiniInstallerFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateRunCrostiniInstallerFunction::Run() {
DVLOG(1) << "AutotestPrivateInstallCrostiniFunction";
Profile* profile = Profile::FromBrowserContext(browser_context());
if (!crostini::CrostiniFeatures::Get()->IsAllowedNow(profile)) {
return RespondNow(Error(kCrostiniNotAvailableForCurrentUserError));
}
// Run GUI installer which will install crostini vm / container and
// start terminal app on completion. After starting the installer,
// we call RestartCrostini and we will be put in the pending restarters
// queue and be notified on success/otherwise of installation.
ash::CrostiniInstallerDialog::Show(
profile,
base::BindOnce([](base::WeakPtr<ash::CrostiniInstallerUI> installer_ui) {
installer_ui->ClickInstallForTesting();
}));
crostini::CrostiniManager::GetForProfile(profile)->RestartCrostini(
crostini::DefaultContainerId(),
base::BindOnce(
&AutotestPrivateRunCrostiniInstallerFunction::CrostiniRestarted,
this));
return RespondLater();
}
void AutotestPrivateRunCrostiniInstallerFunction::CrostiniRestarted(
crostini::CrostiniResult result) {
if (result == crostini::CrostiniResult::SUCCESS) {
Respond(NoArguments());
} else {
Respond(Error("Error installing crostini"));
}
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateRunCrostiniUninstallerFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateRunCrostiniUninstallerFunction::
~AutotestPrivateRunCrostiniUninstallerFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateRunCrostiniUninstallerFunction::Run() {
DVLOG(1) << "AutotestPrivateRunCrostiniUninstallerFunction";
Profile* profile = Profile::FromBrowserContext(browser_context());
if (!crostini::CrostiniFeatures::Get()->IsAllowedNow(profile)) {
return RespondNow(Error(kCrostiniNotAvailableForCurrentUserError));
}
// Run GUI uninstaller which will remove crostini vm / container. We then
// receive the callback with the result when that is complete.
crostini::CrostiniManager::GetForProfile(profile)->AddRemoveCrostiniCallback(
base::BindOnce(
&AutotestPrivateRunCrostiniUninstallerFunction::CrostiniRemoved,
this));
CrostiniUninstallerView::Show(profile);
CrostiniUninstallerView::GetActiveViewForTesting()->Accept();
return RespondLater();
}
void AutotestPrivateRunCrostiniUninstallerFunction::CrostiniRemoved(
crostini::CrostiniResult result) {
if (result == crostini::CrostiniResult::SUCCESS) {
Respond(NoArguments());
} else {
Respond(Error("Error uninstalling crostini"));
}
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateExportCrostiniFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateExportCrostiniFunction::
~AutotestPrivateExportCrostiniFunction() = default;
ExtensionFunction::ResponseAction AutotestPrivateExportCrostiniFunction::Run() {
std::optional<api::autotest_private::ExportCrostini::Params> params =
api::autotest_private::ExportCrostini::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateExportCrostiniFunction " << params->path;
Profile* profile = Profile::FromBrowserContext(browser_context());
if (!crostini::CrostiniFeatures::Get()->IsAllowedNow(profile) ||
!crostini::CrostiniFeatures::Get()->IsExportImportUIAllowed(profile)) {
return RespondNow(Error(kCrostiniNotAvailableForCurrentUserError));
}
base::FilePath path(params->path);
if (path.ReferencesParent()) {
return RespondNow(Error("Invalid export path must not reference parent"));
}
crostini::CrostiniExportImport::GetForProfile(profile)->ExportContainer(
crostini::DefaultContainerId(),
file_manager::util::GetDownloadsFolderForProfile(profile).Append(path),
base::BindOnce(&AutotestPrivateExportCrostiniFunction::CrostiniExported,
this));
return RespondLater();
}
void AutotestPrivateExportCrostiniFunction::CrostiniExported(
crostini::CrostiniResult result) {
if (result == crostini::CrostiniResult::SUCCESS) {
Respond(NoArguments());
} else {
Respond(Error("Error exporting crostini"));
}
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateImportCrostiniFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateImportCrostiniFunction::
~AutotestPrivateImportCrostiniFunction() = default;
ExtensionFunction::ResponseAction AutotestPrivateImportCrostiniFunction::Run() {
std::optional<api::autotest_private::ImportCrostini::Params> params =
api::autotest_private::ImportCrostini::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateImportCrostiniFunction " << params->path;
Profile* profile = Profile::FromBrowserContext(browser_context());
if (!crostini::CrostiniFeatures::Get()->IsAllowedNow(profile) ||
!crostini::CrostiniFeatures::Get()->IsExportImportUIAllowed(profile)) {
return RespondNow(Error(kCrostiniNotAvailableForCurrentUserError));
}
base::FilePath path(params->path);
if (path.ReferencesParent()) {
return RespondNow(Error("Invalid import path must not reference parent"));
}
crostini::CrostiniExportImport::GetForProfile(profile)->ImportContainer(
crostini::DefaultContainerId(),
file_manager::util::GetDownloadsFolderForProfile(profile).Append(path),
base::BindOnce(&AutotestPrivateImportCrostiniFunction::CrostiniImported,
this));
return RespondLater();
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateCouldAllowCrostiniFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateCouldAllowCrostiniFunction::
~AutotestPrivateCouldAllowCrostiniFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateCouldAllowCrostiniFunction::Run() {
DVLOG(1) << "AutotestPrivateCouldAllowCrostiniFunction";
Profile* profile = Profile::FromBrowserContext(browser_context());
return RespondNow(WithArguments(
crostini::CrostiniFeatures::Get()->CouldBeAllowed(profile)));
}
void AutotestPrivateImportCrostiniFunction::CrostiniImported(
crostini::CrostiniResult result) {
if (result == crostini::CrostiniResult::SUCCESS) {
Respond(NoArguments());
} else {
Respond(Error("Error importing crostini"));
}
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetPluginVMPolicyFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetPluginVMPolicyFunction::
~AutotestPrivateSetPluginVMPolicyFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetPluginVMPolicyFunction::Run() {
std::optional<api::autotest_private::SetPluginVMPolicy::Params> params =
api::autotest_private::SetPluginVMPolicy::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateSetPluginVMPolicyFunction " << params->image_url
<< ", " << params->image_hash << ", " << params->license_key;
Profile* profile = Profile::FromBrowserContext(browser_context());
plugin_vm::SetFakePluginVmPolicy(profile, params->image_url,
params->image_hash, params->license_key);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateShowPluginVMInstallerFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateShowPluginVMInstallerFunction::
~AutotestPrivateShowPluginVMInstallerFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateShowPluginVMInstallerFunction::Run() {
DVLOG(1) << "AutotestPrivateShowPluginVMInstallerFunction";
Profile* profile = Profile::FromBrowserContext(browser_context());
plugin_vm::ShowPluginVmInstallerView(profile);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateInstallBorealisFunction
///////////////////////////////////////////////////////////////////////////////
class AutotestPrivateInstallBorealisFunction::InstallationObserver
: public borealis::BorealisInstaller::Observer {
public:
InstallationObserver(
Profile* profile,
base::OnceCallback<void(std::string)> completion_callback)
: observation_(this),
completion_callback_(std::move(completion_callback)) {
observation_.Observe(
&borealis::BorealisService::GetForProfile(profile)->Installer());
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(
[](Profile* profile) {
borealis::BorealisService::GetForProfile(profile)
->Installer()
.Start();
},
profile));
}
void OnProgressUpdated(double fraction_complete) override {}
void OnStateUpdated(
borealis::BorealisInstaller::InstallingState new_state) override {}
void OnInstallationEnded(borealis::mojom::InstallResult result,
const std::string& error_description) override {
std::move(completion_callback_)
.Run(result == borealis::mojom::InstallResult::kSuccess
? ""
: "Failed to install Borealis: " + error_description);
}
void OnCancelInitiated() override {}
private:
base::ScopedObservation<borealis::BorealisInstaller,
borealis::BorealisInstaller::Observer>
observation_;
base::OnceCallback<void(std::string)> completion_callback_;
};
AutotestPrivateInstallBorealisFunction::
AutotestPrivateInstallBorealisFunction() = default;
AutotestPrivateInstallBorealisFunction::
~AutotestPrivateInstallBorealisFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateInstallBorealisFunction::Run() {
Profile* profile = Profile::FromBrowserContext(browser_context());
installation_observer_ = std::make_unique<InstallationObserver>(
profile,
base::BindOnce(&AutotestPrivateInstallBorealisFunction::Complete, this));
return RespondLater();
}
void AutotestPrivateInstallBorealisFunction::Complete(
std::string error_or_empty) {
if (error_or_empty.empty()) {
Respond(NoArguments());
} else {
Respond(Error(error_or_empty));
}
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateRegisterComponentFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateRegisterComponentFunction::
~AutotestPrivateRegisterComponentFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateRegisterComponentFunction::Run() {
std::optional<api::autotest_private::RegisterComponent::Params> params =
api::autotest_private::RegisterComponent::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateRegisterComponentFunction " << params->name
<< ", " << params->path;
g_browser_process->platform_part()
->component_manager_ash()
->RegisterCompatiblePath(params->name,
component_updater::CompatibleComponentInfo(
base::FilePath(params->path), std::nullopt));
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateTakeScreenshotFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateTakeScreenshotFunction::
~AutotestPrivateTakeScreenshotFunction() = default;
ExtensionFunction::ResponseAction AutotestPrivateTakeScreenshotFunction::Run() {
DVLOG(1) << "AutotestPrivateTakeScreenshotFunction";
auto grabber = std::make_unique<ui::ScreenshotGrabber>();
auto* const grabber_ptr = grabber.get();
// TODO(mash): Fix for mash, http://crbug.com/557397
aura::Window* primary_root = ash::Shell::GetPrimaryRootWindow();
// Pass the ScreenshotGrabber to the callback so that it stays alive for the
// duration of the operation, it'll then get deallocated when the callback
// completes.
grabber_ptr->TakeScreenshot(
primary_root, primary_root->bounds(),
base::BindOnce(&AutotestPrivateTakeScreenshotFunction::ScreenshotTaken,
this, std::move(grabber)));
return RespondLater();
}
void AutotestPrivateTakeScreenshotFunction::ScreenshotTaken(
std::unique_ptr<ui::ScreenshotGrabber> grabber,
ui::ScreenshotResult screenshot_result,
scoped_refptr<base::RefCountedMemory> png_data) {
if (screenshot_result != ui::ScreenshotResult::SUCCESS) {
return Respond(Error(base::StrCat(
{"Error taking screenshot ",
base::NumberToString(static_cast<int>(screenshot_result))})));
}
Respond(WithArguments(GetPngDataAsString(png_data)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateTakeScreenshotForDisplayFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateTakeScreenshotForDisplayFunction::
~AutotestPrivateTakeScreenshotForDisplayFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateTakeScreenshotForDisplayFunction::Run() {
std::optional<api::autotest_private::TakeScreenshotForDisplay::Params>
params = api::autotest_private::TakeScreenshotForDisplay::Params::Create(
args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateTakeScreenshotForDisplayFunction "
<< params->display_id;
int64_t target_display_id;
base::StringToInt64(params->display_id, &target_display_id);
auto grabber = std::make_unique<ui::ScreenshotGrabber>();
for (aura::Window* const window : ash::Shell::GetAllRootWindows()) {
const int64_t display_id =
display::Screen::GetScreen()->GetDisplayNearestWindow(window).id();
if (display_id == target_display_id) {
auto* const grabber_ptr = grabber.get();
grabber_ptr->TakeScreenshot(
window, window->bounds(),
base::BindOnce(
&AutotestPrivateTakeScreenshotForDisplayFunction::ScreenshotTaken,
this, std::move(grabber)));
return RespondLater();
}
}
return RespondNow(Error(base::StrCat(
{"Error taking screenshot for display ", params->display_id})));
}
void AutotestPrivateTakeScreenshotForDisplayFunction::ScreenshotTaken(
std::unique_ptr<ui::ScreenshotGrabber> grabber,
ui::ScreenshotResult screenshot_result,
scoped_refptr<base::RefCountedMemory> png_data) {
if (screenshot_result != ui::ScreenshotResult::SUCCESS) {
return Respond(Error(base::StrCat(
{"Error taking screenshot ",
base::NumberToString(static_cast<int>(screenshot_result))})));
}
Respond(WithArguments(GetPngDataAsString(png_data)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetPrinterListFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetPrinterListFunction::AutotestPrivateGetPrinterListFunction() =
default;
AutotestPrivateGetPrinterListFunction::
~AutotestPrivateGetPrinterListFunction() {
DCHECK(!printers_manager_);
}
ExtensionFunction::ResponseAction AutotestPrivateGetPrinterListFunction::Run() {
// |printers_manager_| should be created on UI thread.
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DVLOG(1) << "AutotestPrivateGetPrinterListFunction";
Profile* profile = Profile::FromBrowserContext(browser_context());
printers_manager_ = ash::CupsPrintersManager::Create(profile);
printers_manager_->AddObserver(this);
// Set up a timer to finish waiting after 10 seconds
timeout_timer_.Start(
FROM_HERE, base::Seconds(10),
base::BindOnce(
&AutotestPrivateGetPrinterListFunction::RespondWithTimeoutError,
this));
return RespondLater();
}
void AutotestPrivateGetPrinterListFunction::DestroyPrintersManager() {
// |printers_manager_| should be destroyed on UI thread.
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!printers_manager_) {
return;
}
printers_manager_->RemoveObserver(this);
printers_manager_.reset();
}
void AutotestPrivateGetPrinterListFunction::RespondWithTimeoutError() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (did_respond()) {
return;
}
DestroyPrintersManager();
Respond(
Error("Timeout occurred before Enterprise printers were initialized"));
}
void AutotestPrivateGetPrinterListFunction::RespondWithSuccess() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (did_respond()) {
return;
}
timeout_timer_.AbandonAndStop();
DestroyPrintersManager();
Respond(WithArguments(std::move(results_)));
}
void AutotestPrivateGetPrinterListFunction::OnEnterprisePrintersInitialized() {
// |printers_manager_| should call this on UI thread.
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
constexpr PrinterClass kClassesToFetch[] = {
PrinterClass::kEnterprise,
PrinterClass::kSaved,
PrinterClass::kAutomatic,
};
// We are ready to get the list of printers and finish.
for (const auto& type : kClassesToFetch) {
std::vector<chromeos::Printer> printer_list =
printers_manager_->GetPrinters(type);
for (const auto& printer : printer_list) {
results_.Append(base::Value::Dict()
.Set("printerName", printer.display_name())
.Set("printerId", printer.id())
.Set("printerType", GetPrinterType(type)));
}
}
// We have to respond in separate task on the same thread, because it will
// cause a destruction of CupsPrintersManager which needs to happen after
// we return and on the same thread.
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(&AutotestPrivateGetPrinterListFunction::RespondWithSuccess,
this));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateUpdatePrinterFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateUpdatePrinterFunction::~AutotestPrivateUpdatePrinterFunction() =
default;
ExtensionFunction::ResponseAction AutotestPrivateUpdatePrinterFunction::Run() {
std::optional<api::autotest_private::UpdatePrinter::Params> params =
api::autotest_private::UpdatePrinter::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateUpdatePrinterFunction";
const api::autotest_private::Printer& js_printer = params->printer;
chromeos::Printer printer(js_printer.printer_id ? *js_printer.printer_id
: "");
printer.set_display_name(js_printer.printer_name);
if (js_printer.printer_desc) {
printer.set_description(*js_printer.printer_desc);
}
if (js_printer.printer_make_and_model) {
printer.set_make_and_model(*js_printer.printer_make_and_model);
}
if (js_printer.printer_uri) {
std::string message;
if (!printer.SetUri(*js_printer.printer_uri, &message)) {
LOG(ERROR) << message;
return RespondNow(Error("Incorrect URI: " + message));
}
}
if (js_printer.printer_ppd) {
const GURL ppd =
net::FilePathToFileURL(base::FilePath(*js_printer.printer_ppd));
if (ppd.is_valid()) {
printer.mutable_ppd_reference()->user_supplied_ppd_url = ppd.spec();
} else {
LOG(ERROR) << "Invalid ppd path: " << *js_printer.printer_ppd;
}
}
auto* printers_manager =
ash::CupsPrintersManagerFactory::GetForBrowserContext(browser_context());
printers_manager->SavePrinter(printer);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateRemovePrinterFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateRemovePrinterFunction::~AutotestPrivateRemovePrinterFunction() =
default;
ExtensionFunction::ResponseAction AutotestPrivateRemovePrinterFunction::Run() {
std::optional<api::autotest_private::RemovePrinter::Params> params =
api::autotest_private::RemovePrinter::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateRemovePrinterFunction " << params->printer_id;
auto* printers_manager =
ash::CupsPrintersManagerFactory::GetForBrowserContext(browser_context());
printers_manager->RemoveSavedPrinter(params->printer_id);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateLoadSmartDimComponentFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateLoadSmartDimComponentFunction::
AutotestPrivateLoadSmartDimComponentFunction() = default;
AutotestPrivateLoadSmartDimComponentFunction::
~AutotestPrivateLoadSmartDimComponentFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateLoadSmartDimComponentFunction::Run() {
DVLOG(1) << "AutotestPrivateLoadSmartDimComponentFunction";
if (ash::power::ml::SmartDimMlAgent::GetInstance()->IsDownloadWorkerReady()) {
return RespondNow(NoArguments());
}
const std::string crx_id =
component_updater::SmartDimComponentInstallerPolicy::GetExtensionId();
g_browser_process->component_updater()->GetOnDemandUpdater().OnDemandUpdate(
crx_id, component_updater::OnDemandUpdater::Priority::FOREGROUND,
base::BindOnce(&AutotestPrivateLoadSmartDimComponentFunction::
OnComponentUpdatedCallback,
this));
timer_.Start(
FROM_HERE, base::Seconds(5),
base::BindRepeating(
&AutotestPrivateLoadSmartDimComponentFunction::TryRespond, this));
return RespondLater();
}
void AutotestPrivateLoadSmartDimComponentFunction::OnComponentUpdatedCallback(
update_client::Error error) {
if (error != update_client::Error::NONE &&
error != update_client::Error::UPDATE_IN_PROGRESS) {
Respond(Error(base::StringPrintf(
"On demand update of the SmartDim component failed with error: %d.",
static_cast<int>(error))));
}
}
void AutotestPrivateLoadSmartDimComponentFunction::TryRespond() {
++timer_triggered_count_;
if (did_respond()) {
return;
}
if (ash::power::ml::SmartDimMlAgent::GetInstance()->IsDownloadWorkerReady()) {
Respond(NoArguments());
} else if (timer_triggered_count_ >= 48 /* 48 * 5 sec == 4 minutes */) {
Respond(Error("Timeout occurred before SmartDim component was loaded."));
} else {
timer_.Reset();
}
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetAssistantEnabled
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetAssistantEnabledFunction::
AutotestPrivateSetAssistantEnabledFunction() {
// |AddObserver| will immediately trigger |OnAssistantStatusChanged|.
ash::AssistantState::Get()->AddObserver(this);
}
AutotestPrivateSetAssistantEnabledFunction::
~AutotestPrivateSetAssistantEnabledFunction() {
ash::AssistantState::Get()->RemoveObserver(this);
}
ExtensionFunction::ResponseAction
AutotestPrivateSetAssistantEnabledFunction::Run() {
DVLOG(1) << "AutotestPrivateSetAssistantEnabledFunction";
std::optional<api::autotest_private::SetAssistantEnabled::Params> params =
api::autotest_private::SetAssistantEnabled::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
Profile* profile = Profile::FromBrowserContext(browser_context());
const std::string& err_msg =
SetAllowedPref(profile, ash::assistant::prefs::kAssistantEnabled,
base::Value(params->enabled));
if (!err_msg.empty()) {
return RespondNow(Error(err_msg));
}
// Any state that's not |NOT_READY| would be considered a ready state.
const bool not_ready = (ash::AssistantState::Get()->assistant_status() ==
ash::assistant::AssistantStatus::NOT_READY);
const bool success = (params->enabled != not_ready);
if (success) {
return RespondNow(NoArguments());
}
// Assistant service has not responded yet, set up a delayed timer to wait for
// it and holder a reference to |this|. Also make sure we stop and respond
// when timeout.
enabled_ = params->enabled;
timeout_timer_.Start(
FROM_HERE, base::Milliseconds(params->timeout_ms),
base::BindOnce(&AutotestPrivateSetAssistantEnabledFunction::Timeout,
this));
return RespondLater();
}
void AutotestPrivateSetAssistantEnabledFunction::OnAssistantStatusChanged(
ash::assistant::AssistantStatus status) {
// Must check if the Optional contains value first to avoid possible
// segmentation fault caused by Respond() below being called before
// RespondLater() in Run(). This will happen due to AddObserver() call
// in the constructor will trigger this function immediately.
if (!enabled_.has_value()) {
return;
}
const bool not_ready = (status == ash::assistant::AssistantStatus::NOT_READY);
const bool success = (enabled_.value() != not_ready);
if (!success) {
return;
}
Respond(NoArguments());
enabled_.reset();
timeout_timer_.AbandonAndStop();
}
void AutotestPrivateSetAssistantEnabledFunction::Timeout() {
DCHECK(!did_respond());
Respond(Error("Assistant service timed out"));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateEnableAssistantAndWaitForReadyFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateEnableAssistantAndWaitForReadyFunction::
AutotestPrivateEnableAssistantAndWaitForReadyFunction() = default;
AutotestPrivateEnableAssistantAndWaitForReadyFunction::
~AutotestPrivateEnableAssistantAndWaitForReadyFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateEnableAssistantAndWaitForReadyFunction::Run() {
DVLOG(1) << "AutotestPrivateEnableAssistantAndWaitForReadyFunction";
if (ash::AssistantState::Get()->assistant_status() ==
ash::assistant::AssistantStatus::READY) {
return RespondNow(Error("Assistant is already enabled."));
}
// We can set this callback only when assistant status is NOT_READY. We should
// call this before we try to enable Assistant to avoid causing some timing
// issue.
ash::assistant::AssistantManagerServiceImpl::
SetInitializedInternalCallbackForTesting(base::BindOnce(
&AutotestPrivateEnableAssistantAndWaitForReadyFunction::
OnInitializedInternal,
this));
Profile* profile = Profile::FromBrowserContext(browser_context());
const std::string& err_msg = SetAllowedPref(
profile, ash::assistant::prefs::kAssistantEnabled, base::Value(true));
if (!err_msg.empty()) {
return RespondNow(Error(err_msg));
}
return RespondLater();
}
void AutotestPrivateEnableAssistantAndWaitForReadyFunction::
OnInitializedInternal() {
Respond(NoArguments());
}
// AssistantInteractionHelper is a helper class used to interact with Assistant
// server and store interaction states for tests. It is shared by
// |AutotestPrivateSendAssistantTextQueryFunction| and
// |AutotestPrivateWaitForAssistantQueryStatusFunction|.
class AssistantInteractionHelper
: public ash::assistant::AssistantInteractionSubscriber {
public:
using OnInteractionFinishedCallback =
base::OnceCallback<void(const std::optional<std::string>& error)>;
AssistantInteractionHelper() = default;
AssistantInteractionHelper(const AssistantInteractionHelper&) = delete;
AssistantInteractionHelper& operator=(const AssistantInteractionHelper&) =
delete;
~AssistantInteractionHelper() override {
if (GetAssistant()) {
GetAssistant()->RemoveAssistantInteractionSubscriber(this);
}
}
void Init(OnInteractionFinishedCallback on_interaction_finished_callback) {
// Subscribe to Assistant interaction events.
GetAssistant()->AddAssistantInteractionSubscriber(this);
on_interaction_finished_callback_ =
std::move(on_interaction_finished_callback);
}
void SendTextQuery(const std::string& query, bool allow_tts) {
// Start text interaction with Assistant server.
GetAssistant()->StartTextInteraction(
query, ash::assistant::AssistantQuerySource::kUnspecified, allow_tts);
query_status_.Set("queryText", query);
}
base::Value::Dict GetQueryStatus() { return std::move(query_status_); }
ash::assistant::Assistant* GetAssistant() {
auto* assistant_service = ash::assistant::AssistantService::Get();
return assistant_service ? assistant_service->GetAssistant() : nullptr;
}
private:
// ash::assistant::AssistantInteractionSubscriber:
using AssistantSuggestion = ash::assistant::AssistantSuggestion;
using AssistantInteractionMetadata =
ash::assistant::AssistantInteractionMetadata;
using AssistantInteractionResolution =
ash::assistant::AssistantInteractionResolution;
void OnInteractionStarted(
const AssistantInteractionMetadata& metadata) override {
const bool is_voice_interaction =
ash::assistant::AssistantInteractionType::kVoice == metadata.type;
query_status_.Set("isMicOpen", is_voice_interaction);
interaction_in_progress_ = true;
}
void OnInteractionFinished(
AssistantInteractionResolution resolution) override {
// If you send an Assistant text query while another query is already
// running, OnInteractionFinished can be called for it. We have to subscribe
// assistant interactions before sending a text query as it can trigger
// OnInteractionStarted.
//
// e.g.
// 1. autotestPrivate.sendAssistantTextQuery("your query", ...).
// 2. AutoTestPrivate starts listening Assistant interactions.
// 3. AutoTestPrivate sends the text query to Assistant.
// 4. Assistant cancels the on-going query. -> OnInteractionFinished
// 5. Assistant starts the new query. -> OnInteractionStarted
if (!interaction_in_progress_) {
DVLOG(1) << "Ignoring an uninterested OnInteractionFinished call";
return;
}
interaction_in_progress_ = false;
CHECK(on_interaction_finished_callback_)
<< "on_interaction_finished_callback_ is not set.";
if (resolution == AssistantInteractionResolution::kError) {
SendErrorResponse(
base::StringPrintf("Interaction closed with resolution %s",
ResolutionToString(resolution).c_str()));
return;
}
// Only invoke the callback when |result_| is not empty to avoid an early
// return before the entire session is completed. This happens when
// sending queries to modify device settings, e.g. "turn on bluetooth",
// which results in a round trip due to the need to fetch device state
// on the client and return that to the server as part of a follow-up
// interaction.
if (result_.empty()) {
return;
}
if (resolution != AssistantInteractionResolution::kNormal) {
SendErrorResponse(
base::StringPrintf("Interaction closed with resolution %s",
ResolutionToString(resolution).c_str()));
return;
}
query_status_.Set("queryResponse", std::move(result_));
SendSuccessResponse();
}
void OnHtmlResponse(const std::string& response,
const std::string& fallback) override {
result_.Set("htmlResponse", response);
CheckResponseIsValid(__FUNCTION__);
}
void OnTextResponse(const std::string& response) override {
result_.Set("text", response);
CheckResponseIsValid(__FUNCTION__);
}
void OnOpenUrlResponse(const ::GURL& url, bool in_background) override {
result_.Set("openUrl", url.possibly_invalid_spec());
}
void OnOpenAppResponse(
const ash::assistant::AndroidAppInfo& app_info) override {
result_.Set("openAppResponse", app_info.package_name);
CheckResponseIsValid(__FUNCTION__);
}
void OnSpeechRecognitionFinalResult(
const std::string& final_result) override {
query_status_.Set("queryText", final_result);
}
void CheckResponseIsValid(const std::string& function_name) {
if (!interaction_in_progress_) {
// We should only get a response while the interaction is open
// (started and not finished).
SendErrorResponse(function_name +
" was called after the interaction was closed");
}
}
void SendSuccessResponse() {
std::move(on_interaction_finished_callback_).Run(std::nullopt);
}
void SendErrorResponse(const std::string& error) {
std::move(on_interaction_finished_callback_).Run(error);
}
base::Value::Dict query_status_;
base::Value::Dict result_;
bool interaction_in_progress_ = false;
// Callback triggered when interaction finished with non-empty response.
OnInteractionFinishedCallback on_interaction_finished_callback_;
};
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSendAssistantTextQueryFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSendAssistantTextQueryFunction::
AutotestPrivateSendAssistantTextQueryFunction()
: interaction_helper_(std::make_unique<AssistantInteractionHelper>()) {}
AutotestPrivateSendAssistantTextQueryFunction::
~AutotestPrivateSendAssistantTextQueryFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSendAssistantTextQueryFunction::Run() {
DVLOG(1) << "AutotestPrivateSendAssistantTextQueryFunction";
std::optional<api::autotest_private::SendAssistantTextQuery::Params> params =
api::autotest_private::SendAssistantTextQuery::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
Profile* profile = Profile::FromBrowserContext(browser_context());
ash::assistant::AssistantAllowedState allowed_state =
assistant::IsAssistantAllowedForProfile(profile);
if (allowed_state != ash::assistant::AssistantAllowedState::ALLOWED) {
return RespondNow(Error(base::StringPrintf(
"Assistant not allowed - state: %d", allowed_state)));
}
session_manager::SessionState session_state =
session_manager::SessionManager::Get()->session_state();
if (session_state != session_manager::SessionState::ACTIVE) {
// tast side code matches with this error string, i.e. update both if you
// change this.
return RespondNow(
Error("Session state must be ACTIVE to send a text query. Session "
"state was *",
ToString(session_state)));
}
interaction_helper_->Init(
base::BindOnce(&AutotestPrivateSendAssistantTextQueryFunction::
OnInteractionFinishedCallback,
this));
// Start text interaction with Assistant server.
interaction_helper_->SendTextQuery(params->query, /*allow_tts=*/false);
// Set up a delayed timer to wait for the query response and hold a reference
// to |this| to avoid being destructed. Also make sure we stop and respond
// when timeout.
timeout_timer_.Start(
FROM_HERE, base::Milliseconds(params->timeout_ms),
base::BindOnce(&AutotestPrivateSendAssistantTextQueryFunction::Timeout,
this));
return RespondLater();
}
void AutotestPrivateSendAssistantTextQueryFunction::
OnInteractionFinishedCallback(const std::optional<std::string>& error) {
DCHECK(!did_respond());
if (error) {
Respond(Error(error.value()));
} else {
Respond(WithArguments(interaction_helper_->GetQueryStatus()));
}
// |timeout_timer_| need to be hold until |Respond(.)| is called to avoid
// |this| being destructed.
timeout_timer_.AbandonAndStop();
}
void AutotestPrivateSendAssistantTextQueryFunction::Timeout() {
DCHECK(!did_respond());
Respond(Error("Assistant response timeout."));
// Reset to unsubscribe OnInteractionFinishedCallback().
interaction_helper_.reset();
}
std::string AutotestPrivateSendAssistantTextQueryFunction::ToString(
session_manager::SessionState session_state) {
switch (session_state) {
case session_manager::SessionState::UNKNOWN:
return "UNKNOWN";
case session_manager::SessionState::OOBE:
return "OOBE";
case session_manager::SessionState::LOGIN_PRIMARY:
return "LOGIN_PRIMARY";
case session_manager::SessionState::LOGGED_IN_NOT_ACTIVE:
return "LOGGED_IN_NOT_ACTIVE";
case session_manager::SessionState::ACTIVE:
return "ACTIVE";
case session_manager::SessionState::LOCKED:
return "LOCKED";
case session_manager::SessionState::LOGIN_SECONDARY:
return "LOGIN_SECONDARY";
case session_manager::SessionState::RMA:
return "RMA";
}
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateWaitForAssistantQueryStatusFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateWaitForAssistantQueryStatusFunction::
AutotestPrivateWaitForAssistantQueryStatusFunction()
: interaction_helper_(std::make_unique<AssistantInteractionHelper>()) {}
AutotestPrivateWaitForAssistantQueryStatusFunction::
~AutotestPrivateWaitForAssistantQueryStatusFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateWaitForAssistantQueryStatusFunction::Run() {
DVLOG(1) << "AutotestPrivateWaitForAssistantQueryStatusFunction";
std::optional<api::autotest_private::WaitForAssistantQueryStatus::Params>
params =
api::autotest_private::WaitForAssistantQueryStatus::Params::Create(
args());
EXTENSION_FUNCTION_VALIDATE(params);
Profile* profile = Profile::FromBrowserContext(browser_context());
ash::assistant::AssistantAllowedState allowed_state =
assistant::IsAssistantAllowedForProfile(profile);
if (allowed_state != ash::assistant::AssistantAllowedState::ALLOWED) {
return RespondNow(Error(base::StringPrintf(
"Assistant not allowed - state: %d", allowed_state)));
}
interaction_helper_->Init(
base::BindOnce(&AutotestPrivateWaitForAssistantQueryStatusFunction::
OnInteractionFinishedCallback,
this));
// Start waiting for the response before time out.
timeout_timer_.Start(
FROM_HERE, base::Seconds(params->timeout_s),
base::BindOnce(
&AutotestPrivateWaitForAssistantQueryStatusFunction::Timeout, this));
return RespondLater();
}
void AutotestPrivateWaitForAssistantQueryStatusFunction::
OnInteractionFinishedCallback(const std::optional<std::string>& error) {
DCHECK(!did_respond());
if (error) {
Respond(Error(error.value()));
} else {
Respond(WithArguments(interaction_helper_->GetQueryStatus()));
}
// |timeout_timer_| need to be hold until |Respond(.)| is called to avoid
// |this| being destructed.
timeout_timer_.AbandonAndStop();
}
void AutotestPrivateWaitForAssistantQueryStatusFunction::Timeout() {
DCHECK(!did_respond());
Respond(Error("No query response received before time out."));
// Reset to unsubscribe OnInteractionFinishedCallback().
interaction_helper_.reset();
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateIsArcPackageListInitialRefreshedFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateIsArcPackageListInitialRefreshedFunction::
AutotestPrivateIsArcPackageListInitialRefreshedFunction() = default;
AutotestPrivateIsArcPackageListInitialRefreshedFunction::
~AutotestPrivateIsArcPackageListInitialRefreshedFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateIsArcPackageListInitialRefreshedFunction::Run() {
DVLOG(1) << "AutotestPrivateIsArcPackageListInitialRefreshedFunction";
ArcAppListPrefs* const prefs =
ArcAppListPrefs::Get(Profile::FromBrowserContext(browser_context()));
return RespondNow(WithArguments(prefs->package_list_initial_refreshed()));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetAllowedPrefFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetAllowedPrefFunction::
~AutotestPrivateSetAllowedPrefFunction() = default;
ExtensionFunction::ResponseAction AutotestPrivateSetAllowedPrefFunction::Run() {
DVLOG(1) << "AutotestPrivateSetAllowedPrefFunction";
std::optional<api::autotest_private::SetAllowedPref::Params> params =
api::autotest_private::SetAllowedPref::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
const std::string& pref_name = params->pref_name;
const base::Value& value = params->value;
Profile* profile = Profile::FromBrowserContext(browser_context());
const std::string& err_msg = SetAllowedPref(profile, pref_name, value);
if (!err_msg.empty()) {
return RespondNow(Error(err_msg));
}
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateClearAllowedPrefFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateClearAllowedPrefFunction::
~AutotestPrivateClearAllowedPrefFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateClearAllowedPrefFunction::Run() {
std::optional<api::autotest_private::ClearAllowedPref::Params> params =
api::autotest_private::ClearAllowedPref::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
if (!ClearAllowedPref(Profile::FromBrowserContext(browser_context()),
params->pref_name)) {
return RespondNow(
Error("Cannot clear pref absent in allowlist: " + params->pref_name));
}
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetWhitelistedPrefFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetWhitelistedPrefFunction::
~AutotestPrivateSetWhitelistedPrefFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetWhitelistedPrefFunction::Run() {
DVLOG(1) << "AutotestPrivateSetWhitelistedPrefFunction";
std::optional<api::autotest_private::SetAllowedPref::Params> params =
api::autotest_private::SetAllowedPref::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
const std::string& pref_name = params->pref_name;
const base::Value& value = params->value;
Profile* profile = Profile::FromBrowserContext(browser_context());
const std::string& err_msg = SetAllowedPref(profile, pref_name, value);
if (!err_msg.empty()) {
return RespondNow(Error(err_msg));
}
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetCrostiniAppScaledFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetCrostiniAppScaledFunction::
~AutotestPrivateSetCrostiniAppScaledFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetCrostiniAppScaledFunction::Run() {
std::optional<api::autotest_private::SetCrostiniAppScaled::Params> params =
api::autotest_private::SetCrostiniAppScaled::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateSetCrostiniAppScaledFunction " << params->app_id
<< " " << params->scaled;
ChromeShelfController* const controller = ChromeShelfController::instance();
if (!controller) {
return RespondNow(Error("Controller not available"));
}
auto* registry_service =
guest_os::GuestOsRegistryServiceFactory::GetForProfile(
controller->profile());
if (!registry_service) {
return RespondNow(Error("Crostini registry not available"));
}
registry_service->SetAppScaled(params->app_id, params->scaled);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetPrimaryDisplayScaleFactorFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetPrimaryDisplayScaleFactorFunction::
~AutotestPrivateGetPrimaryDisplayScaleFactorFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateGetPrimaryDisplayScaleFactorFunction::Run() {
DVLOG(1) << "AutotestPrivateGetPrimaryDisplayScaleFactorFunction";
display::Display primary_display =
display::Screen::GetScreen()->GetPrimaryDisplay();
float scale_factor = primary_display.device_scale_factor();
return RespondNow(WithArguments(scale_factor));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateIsTabletModeEnabledFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateIsTabletModeEnabledFunction::
~AutotestPrivateIsTabletModeEnabledFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateIsTabletModeEnabledFunction::Run() {
DVLOG(1) << "AutotestPrivateIsTabletModeEnabledFunction";
return RespondNow(
WithArguments(display::Screen::GetScreen()->InTabletMode()));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetTabletModeEnabledFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetTabletModeEnabledFunction::
~AutotestPrivateSetTabletModeEnabledFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetTabletModeEnabledFunction::Run() {
DVLOG(1) << "AutotestPrivateSetTabletModeEnabledFunction";
std::optional<api::autotest_private::SetTabletModeEnabled::Params> params =
api::autotest_private::SetTabletModeEnabled::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
if (display::Screen::GetScreen()->InTabletMode() == params->enabled) {
return RespondNow(
WithArguments(display::Screen::GetScreen()->InTabletMode()));
}
ash::TabletMode::Waiter waiter(params->enabled);
if (!ash::TabletMode::Get()->ForceUiTabletModeState(params->enabled)) {
return RespondNow(Error("failed to switch the tablet mode state"));
}
waiter.Wait();
return RespondNow(
WithArguments(display::Screen::GetScreen()->InTabletMode()));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetAllInstalledAppsFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetAllInstalledAppsFunction::
AutotestPrivateGetAllInstalledAppsFunction() = default;
AutotestPrivateGetAllInstalledAppsFunction::
~AutotestPrivateGetAllInstalledAppsFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateGetAllInstalledAppsFunction::Run() {
DVLOG(1) << "AutotestPrivateGetAllInstalledAppsFunction";
Profile* const profile = Profile::FromBrowserContext(browser_context());
apps::AppServiceProxy* proxy =
apps::AppServiceProxyFactory::GetForProfile(profile);
std::vector<api::autotest_private::App> installed_apps;
proxy->AppRegistryCache().ForEachApp(
[&installed_apps](const apps::AppUpdate& update) {
if (!apps_util::IsInstalled(update.Readiness())) {
return;
}
api::autotest_private::App app;
app.app_id = update.AppId();
// Assume that when `switches::kForceDirectionRTL` is enabled, the
// system language still follows the left-to-right fashion. Because the
// app names carried by `update` are adapted to RTL by inserting extra
// characters that indicate the text direction, we should recover the
// original app names before returning them as the result.
if (base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kForceUIDirection) == switches::kForceDirectionRTL) {
std::u16string name = base::UTF8ToUTF16(update.Name());
base::i18n::UnadjustStringForLocaleDirection(&name);
app.name = base::UTF16ToUTF8(name);
} else {
app.name = update.Name();
}
app.short_name = update.ShortName();
app.publisher_id = update.PublisherId();
app.additional_search_terms = update.AdditionalSearchTerms();
app.type = GetAppType(update.AppType());
app.install_source = GetAppInstallSource(update.InstallReason());
app.readiness = GetAppReadiness(update.Readiness());
app.show_in_launcher = update.ShowInLauncher();
app.show_in_search = update.ShowInSearch();
installed_apps.emplace_back(std::move(app));
});
return RespondNow(
ArgumentList(api::autotest_private::GetAllInstalledApps::Results::Create(
installed_apps)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetShelfItemsFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetShelfItemsFunction::AutotestPrivateGetShelfItemsFunction() =
default;
AutotestPrivateGetShelfItemsFunction::~AutotestPrivateGetShelfItemsFunction() =
default;
ExtensionFunction::ResponseAction AutotestPrivateGetShelfItemsFunction::Run() {
DVLOG(1) << "AutotestPrivateGetShelfItemsFunction";
ChromeShelfController* const controller = ChromeShelfController::instance();
if (!controller) {
return RespondNow(Error("Controller not available"));
}
std::vector<api::autotest_private::ShelfItem> result_items;
for (const auto& item : controller->shelf_model()->items()) {
api::autotest_private::ShelfItem result_item;
result_item.app_id = item.id.app_id;
result_item.launch_id = item.id.launch_id;
result_item.title = base::UTF16ToUTF8(item.title);
result_item.type = GetShelfItemType(item.type);
result_item.status = GetShelfItemStatus(item.status);
result_item.shows_tooltip = item.shows_tooltip;
result_item.pinned_by_policy = item.pinned_by_policy;
result_item.pin_state_forced_by_type = item.pin_state_forced_by_type;
result_item.has_notification = item.has_notification;
result_items.emplace_back(std::move(result_item));
}
return RespondNow(ArgumentList(
api::autotest_private::GetShelfItems::Results::Create(result_items)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetLauncherSearchBoxStateFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetLauncherSearchBoxStateFunction::
AutotestPrivateGetLauncherSearchBoxStateFunction() = default;
AutotestPrivateGetLauncherSearchBoxStateFunction::
~AutotestPrivateGetLauncherSearchBoxStateFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateGetLauncherSearchBoxStateFunction::Run() {
DVLOG(1) << "AutotestPrivateGetLauncherSearchBoxStateFunction";
api::autotest_private::LauncherSearchBoxState launcher_search_box_state;
launcher_search_box_state.ghost_text = ash::GetSearchBoxGhostTextForTest();
return RespondNow(WithArguments(launcher_search_box_state.ToValue()));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetShelfAutoHideBehaviorFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetShelfAutoHideBehaviorFunction::
AutotestPrivateGetShelfAutoHideBehaviorFunction() = default;
AutotestPrivateGetShelfAutoHideBehaviorFunction::
~AutotestPrivateGetShelfAutoHideBehaviorFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateGetShelfAutoHideBehaviorFunction::Run() {
DVLOG(1) << "AutotestPrivateGetShelfAutoHideBehaviorFunction";
std::optional<api::autotest_private::GetShelfAutoHideBehavior::Params>
params = api::autotest_private::GetShelfAutoHideBehavior::Params::Create(
args());
EXTENSION_FUNCTION_VALIDATE(params);
int64_t display_id;
if (!base::StringToInt64(params->display_id, &display_id)) {
return RespondNow(Error(base::StrCat(
{"Invalid display_id; expected string with numbers only, got ",
params->display_id})));
}
Profile* const profile = Profile::FromBrowserContext(browser_context());
ash::ShelfAutoHideBehavior behavior =
ash::GetShelfAutoHideBehaviorPref(profile->GetPrefs(), display_id);
std::string str_behavior;
switch (behavior) {
case ash::ShelfAutoHideBehavior::kAlways:
str_behavior = "always";
break;
case ash::ShelfAutoHideBehavior::kNever:
str_behavior = "never";
break;
case ash::ShelfAutoHideBehavior::kAlwaysHidden:
// SHELF_AUTO_HIDE_ALWAYS_HIDDEN not supported by shelf_prefs.cc
return RespondNow(Error("SHELF_AUTO_HIDE_ALWAYS_HIDDEN not supported"));
}
return RespondNow(WithArguments(std::move(str_behavior)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetShelfAutoHideBehaviorFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetShelfAutoHideBehaviorFunction::
AutotestPrivateSetShelfAutoHideBehaviorFunction() = default;
AutotestPrivateSetShelfAutoHideBehaviorFunction::
~AutotestPrivateSetShelfAutoHideBehaviorFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetShelfAutoHideBehaviorFunction::Run() {
DVLOG(1) << "AutotestPrivateSetShelfAutoHideBehaviorFunction";
std::optional<api::autotest_private::SetShelfAutoHideBehavior::Params>
params = api::autotest_private::SetShelfAutoHideBehavior::Params::Create(
args());
EXTENSION_FUNCTION_VALIDATE(params);
ash::ShelfAutoHideBehavior behavior;
if (params->behavior == "always") {
behavior = ash::ShelfAutoHideBehavior::kAlways;
} else if (params->behavior == "never") {
behavior = ash::ShelfAutoHideBehavior::kNever;
} else {
return RespondNow(Error(
base::StrCat({"Invalid behavior; expected 'always', 'never', got ",
params->behavior})));
}
int64_t display_id;
if (!base::StringToInt64(params->display_id, &display_id)) {
return RespondNow(Error(base::StrCat(
{"Invalid display_id; expected string with numbers only, got ",
params->display_id})));
}
Profile* const profile = Profile::FromBrowserContext(browser_context());
ash::SetShelfAutoHideBehaviorPref(profile->GetPrefs(), display_id, behavior);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetShelfAlignmentFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetShelfAlignmentFunction::
AutotestPrivateGetShelfAlignmentFunction() = default;
AutotestPrivateGetShelfAlignmentFunction::
~AutotestPrivateGetShelfAlignmentFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateGetShelfAlignmentFunction::Run() {
DVLOG(1) << "AutotestPrivateGetShelfAlignmentFunction";
std::optional<api::autotest_private::GetShelfAlignment::Params> params =
api::autotest_private::GetShelfAlignment::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
int64_t display_id;
if (!base::StringToInt64(params->display_id, &display_id)) {
return RespondNow(Error(base::StrCat(
{"Invalid display_id; expected string with numbers only, got ",
params->display_id})));
}
Profile* const profile = Profile::FromBrowserContext(browser_context());
ash::ShelfAlignment alignment =
ash::GetShelfAlignmentPref(profile->GetPrefs(), display_id);
api::autotest_private::ShelfAlignmentType alignment_type;
switch (alignment) {
case ash::ShelfAlignment::kBottom:
alignment_type = api::autotest_private::ShelfAlignmentType::kBottom;
break;
case ash::ShelfAlignment::kLeft:
alignment_type = api::autotest_private::ShelfAlignmentType::kLeft;
break;
case ash::ShelfAlignment::kRight:
alignment_type = api::autotest_private::ShelfAlignmentType::kRight;
break;
case ash::ShelfAlignment::kBottomLocked:
// ShelfAlignment::kBottomLocked not supported by
// shelf_prefs.cc
return RespondNow(Error("ShelfAlignment::kBottomLocked not supported"));
}
return RespondNow(
WithArguments(api::autotest_private::ToString(alignment_type)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetShelfAlignmentFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetShelfAlignmentFunction::
AutotestPrivateSetShelfAlignmentFunction() = default;
AutotestPrivateSetShelfAlignmentFunction::
~AutotestPrivateSetShelfAlignmentFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetShelfAlignmentFunction::Run() {
DVLOG(1) << "AutotestPrivateSetShelfAlignmentFunction";
std::optional<api::autotest_private::SetShelfAlignment::Params> params =
api::autotest_private::SetShelfAlignment::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
ash::ShelfAlignment alignment;
switch (params->alignment) {
case api::autotest_private::ShelfAlignmentType::kBottom:
alignment = ash::ShelfAlignment::kBottom;
break;
case api::autotest_private::ShelfAlignmentType::kLeft:
alignment = ash::ShelfAlignment::kLeft;
break;
case api::autotest_private::ShelfAlignmentType::kRight:
alignment = ash::ShelfAlignment::kRight;
break;
case api::autotest_private::ShelfAlignmentType::kNone:
return RespondNow(
Error("Invalid None alignment; expected 'Bottom', 'Left', or "
"'Right'"));
}
int64_t display_id;
if (!base::StringToInt64(params->display_id, &display_id)) {
return RespondNow(Error(base::StrCat(
{"Invalid display_id; expected string with numbers only, got ",
params->display_id})));
}
Profile* const profile = Profile::FromBrowserContext(browser_context());
ash::SetShelfAlignmentPref(profile->GetPrefs(), display_id, alignment);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateWaitForOverviewStateFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateWaitForOverviewStateFunction::
AutotestPrivateWaitForOverviewStateFunction() = default;
AutotestPrivateWaitForOverviewStateFunction::
~AutotestPrivateWaitForOverviewStateFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateWaitForOverviewStateFunction::Run() {
std::optional<api::autotest_private::WaitForOverviewState::Params> params =
api::autotest_private::WaitForOverviewState::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
const ash::OverviewAnimationState overview_state =
ToOverviewAnimationState(params->overview_state);
ash::OverviewTestApi().WaitForOverviewState(
overview_state,
base::BindOnce(&AutotestPrivateWaitForOverviewStateFunction::Done, this));
return did_respond() ? AlreadyResponded() : RespondLater();
}
void AutotestPrivateWaitForOverviewStateFunction::Done(bool success) {
if (!success) {
Respond(Error("Overview animation was canceled."));
return;
}
Respond(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSendArcOverlayColorFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSendArcOverlayColorFunction::
~AutotestPrivateSendArcOverlayColorFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSendArcOverlayColorFunction::Run() {
DVLOG(1) << "AutotestPrivateSendArcOverlayColorFunction";
std::optional<api::autotest_private::SendArcOverlayColor::Params> params =
api::autotest_private::SendArcOverlayColor::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
arc::ArcSystemUIBridge* const system_ui =
arc::ArcSystemUIBridge::GetForBrowserContext(browser_context());
if (!system_ui) {
return RespondNow(Error("No ARC System UI Bridge is available."));
}
const bool result = system_ui->SendOverlayColor(
params->color, ToThemeStyleType(params->theme));
return RespondNow(WithArguments(result));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetOverviewModeStateFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetOverviewModeStateFunction::
AutotestPrivateSetOverviewModeStateFunction() = default;
AutotestPrivateSetOverviewModeStateFunction::
~AutotestPrivateSetOverviewModeStateFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetOverviewModeStateFunction::Run() {
std::optional<api::autotest_private::SetOverviewModeState::Params> params =
api::autotest_private::SetOverviewModeState::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
ash::OverviewTestApi().SetOverviewMode(
params->start,
base::BindOnce(
&AutotestPrivateSetOverviewModeStateFunction::OnOverviewModeChanged,
this, params->start));
return did_respond() ? AlreadyResponded() : RespondLater();
}
void AutotestPrivateSetOverviewModeStateFunction::OnOverviewModeChanged(
bool for_start,
bool finished) {
auto arg = WithArguments(finished);
// On starting the overview animation, it needs to wait for 1 extra second
// to trigger the occlusion tracker.
if (for_start) {
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&AutotestPrivateSetOverviewModeStateFunction::Respond,
this, std::move(arg)),
base::Seconds(1));
} else {
Respond(std::move(arg));
}
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetDefaultPinnedAppIdsFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetDefaultPinnedAppIdsFunction::
AutotestPrivateGetDefaultPinnedAppIdsFunction() = default;
AutotestPrivateGetDefaultPinnedAppIdsFunction::
~AutotestPrivateGetDefaultPinnedAppIdsFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateGetDefaultPinnedAppIdsFunction::Run() {
std::vector<std::string> default_pinned_app_ids;
for (const char* default_app_id :
GetDefaultPinnedAppsForFormFactor(browser_context())) {
default_pinned_app_ids.emplace_back(default_app_id);
}
return RespondNow(ArgumentList(
api::autotest_private::GetDefaultPinnedAppIds::Results::Create(
default_pinned_app_ids)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateShowVirtualKeyboardIfEnabledFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateShowVirtualKeyboardIfEnabledFunction::
AutotestPrivateShowVirtualKeyboardIfEnabledFunction() = default;
AutotestPrivateShowVirtualKeyboardIfEnabledFunction::
~AutotestPrivateShowVirtualKeyboardIfEnabledFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateShowVirtualKeyboardIfEnabledFunction::Run() {
if (!ash::IMEBridge::Get() ||
!ash::IMEBridge::Get()->GetInputContextHandler() ||
!ash::IMEBridge::Get()->GetInputContextHandler()->GetInputMethod()) {
return RespondNow(NoArguments());
}
ash::IMEBridge::Get()
->GetInputContextHandler()
->GetInputMethod()
->SetVirtualKeyboardVisibilityIfEnabled(true);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateArcAppTracingStartFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateArcAppTracingStartFunction::
AutotestPrivateArcAppTracingStartFunction() = default;
AutotestPrivateArcAppTracingStartFunction::
~AutotestPrivateArcAppTracingStartFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateArcAppTracingStartFunction::Run() {
DVLOG(1) << "AutotestPrivateArcAppTracingStartFunction";
arc::ArcAppPerformanceTracing* const tracing =
arc::ArcAppPerformanceTracing::GetForBrowserContext(browser_context());
if (!tracing) {
return RespondNow(Error("No ARC performance tracing is available."));
}
if (!tracing->StartCustomTracing()) {
return RespondNow(Error("Failed to start custom tracing."));
}
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateArcAppTracingStopAndAnalyzeFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateArcAppTracingStopAndAnalyzeFunction::
AutotestPrivateArcAppTracingStopAndAnalyzeFunction() = default;
AutotestPrivateArcAppTracingStopAndAnalyzeFunction::
~AutotestPrivateArcAppTracingStopAndAnalyzeFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateArcAppTracingStopAndAnalyzeFunction::Run() {
DVLOG(1) << "AutotestPrivateArcAppTracingStopAndAnalyzeFunction";
arc::ArcAppPerformanceTracing* const tracing =
arc::ArcAppPerformanceTracing::GetForBrowserContext(browser_context());
if (!tracing) {
return RespondNow(Error("No ARC performance tracing is available."));
}
return RespondNow(WithArguments(tracing->StopCustomTracing()));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetArcAppWindowFocusFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetArcAppWindowFocusFunction::
AutotestPrivateSetArcAppWindowFocusFunction() = default;
AutotestPrivateSetArcAppWindowFocusFunction::
~AutotestPrivateSetArcAppWindowFocusFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetArcAppWindowFocusFunction::Run() {
std::optional<api::autotest_private::SetArcAppWindowFocus::Params> params =
api::autotest_private::SetArcAppWindowFocus::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateSetArcAppWindowFocusFunction "
<< params->package_name;
aura::Window* arc_window = GetArcAppWindow(params->package_name);
if (!arc_window) {
return RespondNow(Error(base::StrCat(
{"No ARC app window was found for ", params->package_name})));
}
if (!arc_window->CanFocus()) {
return RespondNow(Error(base::StrCat(
{"ARC app window can't focus for ", params->package_name})));
}
// No matter whether it is focused already, set it focused.
arc_window->Focus();
if (!arc_window->HasFocus()) {
return RespondNow(Error(base::StrCat(
{"Failed to set focus for ARC App window ", params->package_name})));
}
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSwapWindowsInSplitViewFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSwapWindowsInSplitViewFunction::
AutotestPrivateSwapWindowsInSplitViewFunction() = default;
AutotestPrivateSwapWindowsInSplitViewFunction::
~AutotestPrivateSwapWindowsInSplitViewFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSwapWindowsInSplitViewFunction::Run() {
ash::SplitViewTestApi().SwapWindows();
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateWaitForDisplayRotationFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateWaitForDisplayRotationFunction::
AutotestPrivateWaitForDisplayRotationFunction() = default;
AutotestPrivateWaitForDisplayRotationFunction::
~AutotestPrivateWaitForDisplayRotationFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateWaitForDisplayRotationFunction::Run() {
DVLOG(1) << "AutotestPrivateWaitForDisplayRotationFunction";
std::optional<api::autotest_private::WaitForDisplayRotation::Params> params =
api::autotest_private::WaitForDisplayRotation::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
if (!base::StringToInt64(params->display_id, &display_id_)) {
return RespondNow(Error(base::StrCat(
{"Invalid display_id; expected string with numbers only, got ",
params->display_id})));
}
if (params->rotation == api::autotest_private::RotationType::kRotateAny) {
display::Display display;
if (!display::Screen::GetScreen()->GetDisplayWithDisplayId(display_id_,
&display)) {
return RespondNow(Error(base::StrCat(
{"Display is not found for display_id ", params->display_id})));
}
DCHECK(display.is_valid());
if (!display.IsInternal()) {
return RespondNow(
Error("RotateAny is valid only for the internal display"));
}
auto* screen_orientation_controller =
ash::Shell::Get()->screen_orientation_controller();
if (screen_orientation_controller->user_rotation_locked()) {
self_ = this;
screen_orientation_controller->AddObserver(this);
return RespondLater();
}
target_rotation_.reset();
} else {
target_rotation_ = ToRotation(params->rotation);
}
auto result = CheckScreenRotationAnimation();
if (result) {
return RespondNow(std::move(*result));
}
return RespondLater();
}
void AutotestPrivateWaitForDisplayRotationFunction::
OnScreenCopiedBeforeRotation() {}
void AutotestPrivateWaitForDisplayRotationFunction::
OnScreenRotationAnimationFinished(ash::ScreenRotationAnimator* animator,
bool canceled) {
animator->RemoveObserver(this);
display::Display display;
display::Screen::GetScreen()->GetDisplayWithDisplayId(display_id_, &display);
Respond(WithArguments(display.is_valid() &&
(!target_rotation_.has_value() ||
display.rotation() == *target_rotation_)));
self_.reset();
}
void AutotestPrivateWaitForDisplayRotationFunction::
OnUserRotationLockChanged() {
auto* screen_orientation_controller =
ash::Shell::Get()->screen_orientation_controller();
if (screen_orientation_controller->user_rotation_locked()) {
return;
}
screen_orientation_controller->RemoveObserver(this);
self_.reset();
target_rotation_.reset();
auto result = CheckScreenRotationAnimation();
// Wait for the rotation if unlocking causes rotation.
if (result) {
Respond(std::move(*result));
}
}
std::optional<ExtensionFunction::ResponseValue>
AutotestPrivateWaitForDisplayRotationFunction::CheckScreenRotationAnimation() {
auto* root_controller =
ash::Shell::GetRootWindowControllerWithDisplayId(display_id_);
if (!root_controller || !root_controller->GetScreenRotationAnimator()) {
return Error(base::StringPrintf(
"Invalid display_id; no root window found for the display id %" PRId64,
display_id_));
}
auto* animator = root_controller->GetScreenRotationAnimator();
if (!animator->IsRotating()) {
display::Display display;
display::Screen::GetScreen()->GetDisplayWithDisplayId(display_id_,
&display);
// This should never fail.
DCHECK(display.is_valid());
return WithArguments(!target_rotation_.has_value() ||
display.rotation() == *target_rotation_);
}
self_ = this;
animator->AddObserver(this);
return std::nullopt;
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetAppWindowListFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetAppWindowListFunction::
AutotestPrivateGetAppWindowListFunction() = default;
AutotestPrivateGetAppWindowListFunction::
~AutotestPrivateGetAppWindowListFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateGetAppWindowListFunction::Run() {
// Use negative number to avoid potential collision with normal use if any.
static int id_count = -10000;
std::optional<ash::OverviewInfo> overview_info =
ash::OverviewTestApi().GetOverviewInfo();
auto window_list = ash::GetAppWindowList();
std::vector<api::autotest_private::AppWindowInfo> result_list;
for (aura::Window* window : window_list) {
if (window->GetId() == aura::Window::kInitialId) {
window->SetId(id_count--);
}
api::autotest_private::AppWindowInfo window_info;
window_info.id = window->GetId();
window_info.name = window->GetName();
window_info.window_type =
GetAppWindowType(window->GetProperty(chromeos::kAppTypeKey));
window_info.state_type =
ToWindowStateType(window->GetProperty(chromeos::kWindowStateTypeKey));
window_info.bounds_in_root =
ToBoundsDictionary(window->GetBoundsInRootWindow());
window_info.target_bounds = ToBoundsDictionary(window->GetTargetBounds());
window_info.display_id = base::NumberToString(
display::Screen::GetScreen()->GetDisplayNearestWindow(window).id());
window_info.title = base::UTF16ToUTF8(window->GetTitle());
// Check for window hiding animations separately because they pertain to
// layers detached from the window.
window_info.is_animating =
window->layer()->GetAnimator()->is_animating() ||
window->GetProperty(wm::kWindowHidingAnimationCountKey) > 0;
window_info.is_visible = window->IsVisible();
window_info.target_visibility = window->TargetVisibility();
window_info.can_focus = window->CanFocus();
window_info.has_focus = window->HasFocus();
window_info.on_active_desk =
chromeos::DesksHelper::Get(window)->BelongsToActiveDesk(window);
window_info.is_active = wm::IsActiveWindow(window);
window_info.has_capture = window->HasCapture();
window_info.can_resize =
(window->GetProperty(aura::client::kResizeBehaviorKey) &
aura::client::kResizeBehaviorCanResize) != 0;
window_info.stacking_order = -1;
// Find the window's stacking order among its siblings.
if (auto* parent = window->parent()) {
const auto& children = parent->children();
auto it = std::find(children.rbegin(), children.rend(), window);
if (it != children.rend()) {
window_info.stacking_order = it - children.rbegin();
}
}
if (window->GetProperty(chromeos::kAppTypeKey) ==
chromeos::AppType::ARC_APP) {
std::string* package_name = window->GetProperty(ash::kArcPackageNameKey);
if (package_name) {
window_info.arc_package_name = *package_name;
} else {
LOG(ERROR) << "The package name for window " << window->GetTitle()
<< " (ID: " << window->GetId()
<< ") isn't available even though it is an ARC window.";
}
}
std::string* full_restore_window_app_id =
window->GetProperty(app_restore::kAppIdKey);
if (full_restore_window_app_id) {
window_info.full_restore_window_app_id = *full_restore_window_app_id;
}
std::string* app_id = window->GetProperty(ash::kAppIDKey);
if (app_id) {
window_info.app_id = *app_id;
}
auto* widget = views::Widget::GetWidgetForNativeWindow(window);
// Frame information
auto* immersive_controller =
chromeos::ImmersiveFullscreenController::Get(widget);
// The widget that hosts the immersive frame can be different from the
// application's widget itself. Use the widget from the immersive
// controller to obtain the FrameHeader.
if (immersive_controller) {
widget = immersive_controller->widget();
}
if (immersive_controller && immersive_controller->IsEnabled()) {
window_info.frame_mode = api::autotest_private::FrameMode::kImmersive;
window_info.is_frame_visible = immersive_controller->IsRevealed();
} else {
window_info.frame_mode = api::autotest_private::FrameMode::kNormal;
window_info.is_frame_visible = IsFrameVisible(widget);
}
auto* frame_header = chromeos::FrameHeader::Get(widget);
if (frame_header) {
window_info.caption_height = frame_header->GetHeaderHeight();
const chromeos::CaptionButtonModel* button_model =
frame_header->GetCaptionButtonModel();
int caption_button_enabled_status = 0;
int caption_button_visible_status = 0;
constexpr views::CaptionButtonIcon all_button_icons[] = {
views::CAPTION_BUTTON_ICON_MINIMIZE,
views::CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE,
views::CAPTION_BUTTON_ICON_CLOSE,
views::CAPTION_BUTTON_ICON_LEFT_TOP_SNAPPED,
views::CAPTION_BUTTON_ICON_RIGHT_BOTTOM_SNAPPED,
views::CAPTION_BUTTON_ICON_BACK,
views::CAPTION_BUTTON_ICON_LOCATION,
views::CAPTION_BUTTON_ICON_MENU,
views::CAPTION_BUTTON_ICON_ZOOM};
for (const auto button : all_button_icons) {
if (button_model->IsEnabled(button)) {
caption_button_enabled_status |= (1 << button);
}
if (button_model->IsVisible(button)) {
caption_button_visible_status |= (1 << button);
}
}
window_info.caption_button_enabled_status = caption_button_enabled_status;
window_info.caption_button_visible_status = caption_button_visible_status;
} else {
auto* no_frame_header_widget =
views::Widget::GetWidgetForNativeWindow(window);
// All widgets for app windows in chromeos should have a frame. Non app
// windows may not have a frame and frame mode will be NONE.
DCHECK(!no_frame_header_widget ||
no_frame_header_widget->GetNativeWindow()->GetType() !=
aura::client::WINDOW_TYPE_NORMAL);
window_info.frame_mode = api::autotest_private::FrameMode::kNone;
window_info.is_frame_visible = false;
}
// Overview info.
if (overview_info.has_value()) {
auto it = overview_info->find(window);
if (it != overview_info->end()) {
window_info.overview_info.emplace();
window_info.overview_info->bounds =
ToBoundsDictionary(it->second.bounds_in_screen);
window_info.overview_info->is_dragged = it->second.is_dragged;
}
}
result_list.emplace_back(std::move(window_info));
}
return RespondNow(ArgumentList(
api::autotest_private::GetAppWindowList::Results::Create(result_list)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetAppWindowStateFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetAppWindowStateFunction::
AutotestPrivateSetAppWindowStateFunction() = default;
AutotestPrivateSetAppWindowStateFunction::
~AutotestPrivateSetAppWindowStateFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetAppWindowStateFunction::Run() {
std::optional<api::autotest_private::SetAppWindowState::Params> params =
api::autotest_private::SetAppWindowState::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateSetAppWindowStateFunction " << params->id;
aura::Window* window = FindAppWindowById(params->id);
if (!window) {
return RespondNow(Error(
base::StringPrintf("No app window was found : id=%d", params->id)));
}
chromeos::WindowStateType expected_state =
GetExpectedWindowState(params->change.event_type);
if (window->GetProperty(chromeos::kWindowStateTypeKey) == expected_state) {
if (params->change.fail_if_no_change &&
*(params->change.fail_if_no_change)) {
return RespondNow(
Error("The app window was already in the expected window state! "));
} else {
return RespondNow(WithArguments(
api::autotest_private::ToString(ToWindowStateType(expected_state))));
}
}
const bool wait = params->wait && *params->wait;
if (wait) {
window_state_observer_ = std::make_unique<WindowStateChangeObserver>(
window, expected_state,
base::BindOnce(
&AutotestPrivateSetAppWindowStateFunction::WindowStateChanged, this,
expected_state));
}
if (params->change.event_type ==
api::autotest_private::WMEventType::kWmeventSnapPrimary ||
params->change.event_type ==
api::autotest_private::WMEventType::kWmeventSnapSecondary) {
const ash::WindowSnapWMEvent event(
ToWMEventType(params->change.event_type));
ash::WindowState::Get(window)->OnWMEvent(&event);
} else {
const ash::WMEvent event(ToWMEventType(params->change.event_type));
ash::WindowState::Get(window)->OnWMEvent(&event);
}
if (!wait) {
return RespondNow(WithArguments(
api::autotest_private::ToString(ToWindowStateType(expected_state))));
}
return RespondLater();
}
void AutotestPrivateSetAppWindowStateFunction::WindowStateChanged(
chromeos::WindowStateType expected_type,
bool success) {
if (!success) {
Respond(Error(
"The app window was destroyed while waiting for its state change! "));
} else {
Respond(WithArguments(
api::autotest_private::ToString(ToWindowStateType(expected_type))));
}
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateActivateAppWindowFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateActivateAppWindowFunction::
~AutotestPrivateActivateAppWindowFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateActivateAppWindowFunction::Run() {
std::optional<api::autotest_private::ActivateAppWindow::Params> params =
api::autotest_private::ActivateAppWindow::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateActivateAppWindowFunction " << params->id;
auto* window = FindAppWindowById(params->id);
if (!window) {
return RespondNow(Error(
base::StringPrintf("No app window was found : id=%d", params->id)));
}
ash::WindowState::Get(window)->Activate();
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateCloseAppWindowFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateCloseAppWindowFunction::
~AutotestPrivateCloseAppWindowFunction() = default;
ExtensionFunction::ResponseAction AutotestPrivateCloseAppWindowFunction::Run() {
std::optional<api::autotest_private::CloseAppWindow::Params> params =
api::autotest_private::CloseAppWindow::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateCloseAppWindowFunction " << params->id;
auto* window = FindAppWindowById(params->id);
if (!window) {
return RespondNow(Error(
base::StringPrintf("No app window was found : id=%d", params->id)));
}
auto* widget = views::Widget::GetWidgetForNativeWindow(window);
widget->Close();
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateInstallPWAForCurrentURL
///////////////////////////////////////////////////////////////////////////////
// Used to notify when when a certain URL contains a PWA.
class AutotestPrivateInstallPWAForCurrentURLFunction::PWABannerObserver
: public webapps::AppBannerManager::Observer {
public:
PWABannerObserver(webapps::AppBannerManager* manager,
base::OnceCallback<void()> callback)
: callback_(std::move(callback)), app_banner_manager_(manager) {
DCHECK(manager);
observation_.Observe(manager);
// If PWA is already loaded, call callback immediately.
Installable installable =
app_banner_manager_->GetInstallableWebAppCheckResult();
if (installable == Installable::kYes_Promotable ||
installable == Installable::kYes_ByUserRequest) {
observation_.Reset();
std::move(callback_).Run();
}
}
PWABannerObserver(const PWABannerObserver&) = delete;
PWABannerObserver& operator=(const PWABannerObserver&) = delete;
~PWABannerObserver() override {}
void OnInstallableWebAppStatusUpdated(
webapps::InstallableWebAppCheckResult result,
const std::optional<webapps::WebAppBannerData>& data) override {
switch (result) {
case Installable::kNo:
[[fallthrough]];
case Installable::kNo_AlreadyInstalled:
[[fallthrough]];
case Installable::kUnknown:
DCHECK(false) << "Unexpected AppBannerManager::Installable value (kNo "
"or kNoAlreadyInstalled or kUnknown)";
break;
case Installable::kYes_Promotable:
[[fallthrough]];
case Installable::kYes_ByUserRequest:
observation_.Reset();
std::move(callback_).Run();
break;
}
}
private:
using Installable = webapps::InstallableWebAppCheckResult;
base::ScopedObservation<webapps::AppBannerManager,
webapps::AppBannerManager::Observer>
observation_{this};
base::OnceCallback<void()> callback_;
raw_ptr<webapps::AppBannerManager> app_banner_manager_;
};
// Used to notify when a PWA is installed.
class AutotestPrivateInstallPWAForCurrentURLFunction::PWAInstallManagerObserver
: public web_app::WebAppInstallManagerObserver {
public:
PWAInstallManagerObserver(
Profile* profile,
base::OnceCallback<void(const webapps::AppId&)> callback)
: provider_(web_app::WebAppProvider::GetForWebApps(profile)),
callback_(std::move(callback)) {
if (!provider_) {
return;
}
provider_->on_registry_ready().Post(
FROM_HERE,
base::BindOnce(&AutotestPrivateInstallPWAForCurrentURLFunction::
PWAInstallManagerObserver::OnProviderReady,
weak_factory_.GetWeakPtr()));
}
PWAInstallManagerObserver(const PWAInstallManagerObserver&) = delete;
PWAInstallManagerObserver& operator=(const PWAInstallManagerObserver&) =
delete;
~PWAInstallManagerObserver() override {}
void OnProviderReady() {
observation_.Observe(&provider_->install_manager());
}
void OnWebAppInstalled(const webapps::AppId& app_id) override {
observation_.Reset();
std::move(callback_).Run(app_id);
}
void OnWebAppInstallManagerDestroyed() override { observation_.Reset(); }
private:
base::ScopedObservation<web_app::WebAppInstallManager,
web_app::WebAppInstallManagerObserver>
observation_{this};
raw_ptr<web_app::WebAppProvider> provider_;
base::OnceCallback<void(const webapps::AppId&)> callback_;
base::WeakPtrFactory<
AutotestPrivateInstallPWAForCurrentURLFunction::PWAInstallManagerObserver>
weak_factory_{this};
};
AutotestPrivateInstallPWAForCurrentURLFunction::
AutotestPrivateInstallPWAForCurrentURLFunction() = default;
AutotestPrivateInstallPWAForCurrentURLFunction::
~AutotestPrivateInstallPWAForCurrentURLFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateInstallPWAForCurrentURLFunction::Run() {
DVLOG(1) << "AutotestPrivateInstallPWAForCurrentURLFunction";
std::optional<api::autotest_private::InstallPWAForCurrentURL::Params> params =
api::autotest_private::InstallPWAForCurrentURL::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
Browser* browser = GetFirstRegularBrowser();
if (!browser) {
return RespondNow(Error("Failed to find regular browser"));
}
content::WebContents* web_contents =
browser->tab_strip_model()->GetActiveWebContents();
webapps::AppBannerManager* app_banner_manager =
webapps::AppBannerManagerDesktop::FromWebContents(web_contents);
if (!app_banner_manager) {
return RespondNow(Error("Failed to create AppBannerManager"));
}
banner_observer_ = std::make_unique<PWABannerObserver>(
app_banner_manager,
base::BindOnce(&AutotestPrivateInstallPWAForCurrentURLFunction::PWALoaded,
this));
// Adding timeout to catch:
// - There is no way to know whether ExecuteCommand fails.
// - Current URL might not have a valid PWA.
timeout_timer_.Start(
FROM_HERE, base::Milliseconds(params->timeout_ms),
base::BindOnce(
&AutotestPrivateInstallPWAForCurrentURLFunction::PWATimeout, this));
return RespondLater();
}
void AutotestPrivateInstallPWAForCurrentURLFunction::PWALoaded() {
Profile* profile = Profile::FromBrowserContext(browser_context());
Browser* browser = GetFirstRegularBrowser();
install_mananger_observer_ = std::make_unique<PWAInstallManagerObserver>(
profile,
base::BindOnce(
&AutotestPrivateInstallPWAForCurrentURLFunction::PWAInstalled, this));
web_app::SetAutoAcceptPWAInstallConfirmationForTesting(true);
if (!chrome::ExecuteCommand(browser, IDC_INSTALL_PWA)) {
return Respond(Error("Failed to execute INSTALL_PWA command"));
}
}
void AutotestPrivateInstallPWAForCurrentURLFunction::PWAInstalled(
const webapps::AppId& app_id) {
web_app::SetAutoAcceptPWAInstallConfirmationForTesting(false);
Respond(WithArguments(app_id));
timeout_timer_.AbandonAndStop();
}
void AutotestPrivateInstallPWAForCurrentURLFunction::PWATimeout() {
web_app::SetAutoAcceptPWAInstallConfirmationForTesting(false);
Respond(Error("Install PWA timed out"));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateActivateAcceleratorFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateActivateAcceleratorFunction::
AutotestPrivateActivateAcceleratorFunction() = default;
AutotestPrivateActivateAcceleratorFunction::
~AutotestPrivateActivateAcceleratorFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateActivateAcceleratorFunction::Run() {
std::optional<api::autotest_private::ActivateAccelerator::Params> params =
api::autotest_private::ActivateAccelerator::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
int modifiers = (params->accelerator.control ? ui::EF_CONTROL_DOWN : 0) |
(params->accelerator.shift ? ui::EF_SHIFT_DOWN : 0) |
(params->accelerator.alt ? ui::EF_ALT_DOWN : 0) |
(params->accelerator.search ? ui::EF_COMMAND_DOWN : 0);
ui::Accelerator accelerator(
StringToKeyCode(params->accelerator.key_code), modifiers,
params->accelerator.pressed ? ui::Accelerator::KeyState::PRESSED
: ui::Accelerator::KeyState::RELEASED);
auto* accelerator_controller = ash::AcceleratorController::Get();
accelerator_controller->GetAcceleratorHistory()->StoreCurrentAccelerator(
accelerator);
accelerator_controller->ApplyAcceleratorForTesting(accelerator); // IN-TEST
if (!accelerator_controller->IsRegistered(accelerator)) {
// If it's not ash accelerator, try aplication's accelerator.
auto* window = GetActiveWindow();
if (!window) {
return RespondNow(
Error(base::StringPrintf("Accelerator is not registered 1")));
}
auto* widget = views::Widget::GetWidgetForNativeWindow(window);
if (!widget) {
return RespondNow(
Error(base::StringPrintf("Accelerator is not registered 2")));
}
bool result = widget->GetFocusManager()->ProcessAccelerator(accelerator);
return RespondNow(WithArguments(result));
}
bool result = accelerator_controller->Process(accelerator);
return RespondNow(WithArguments(result));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateWaitForLauncherStateFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateWaitForLauncherStateFunction::
AutotestPrivateWaitForLauncherStateFunction() = default;
AutotestPrivateWaitForLauncherStateFunction::
~AutotestPrivateWaitForLauncherStateFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateWaitForLauncherStateFunction::Run() {
std::optional<api::autotest_private::WaitForLauncherState::Params> params =
api::autotest_private::WaitForLauncherState::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
auto target_state = ToAppListViewState(params->launcher_state);
// The method is only implemented for fullscreen launcher, for bubble
// launcher, tests use automation APIs to wait for launcher visibility
// changes.
// Exceptionally, allow waiting for kClosed state in clamshell mode, so tests
// can wait for fullscreen launcher state change to finish when exiting tablet
// mode.
if (!display::Screen::GetScreen()->InTabletMode() &&
target_state != ash::AppListViewState::kClosed) {
return RespondNow(Error("Not supported for bubble launcher"));
}
if (WaitForLauncherState(
target_state,
base::BindOnce(&AutotestPrivateWaitForLauncherStateFunction::Done,
this))) {
return AlreadyResponded();
}
return RespondLater();
}
void AutotestPrivateWaitForLauncherStateFunction::Done() {
Respond(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateCreateNewDeskFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateCreateNewDeskFunction::AutotestPrivateCreateNewDeskFunction() =
default;
AutotestPrivateCreateNewDeskFunction::~AutotestPrivateCreateNewDeskFunction() =
default;
ExtensionFunction::ResponseAction AutotestPrivateCreateNewDeskFunction::Run() {
const bool success = ash::AutotestDesksApi().CreateNewDesk();
return RespondNow(WithArguments(success));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateActivateDeskAtIndexFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateActivateDeskAtIndexFunction::
AutotestPrivateActivateDeskAtIndexFunction() = default;
AutotestPrivateActivateDeskAtIndexFunction::
~AutotestPrivateActivateDeskAtIndexFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateActivateDeskAtIndexFunction::Run() {
std::optional<api::autotest_private::ActivateDeskAtIndex::Params> params =
api::autotest_private::ActivateDeskAtIndex::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
if (!ash::AutotestDesksApi().ActivateDeskAtIndex(
params->index,
base::BindOnce(
&AutotestPrivateActivateDeskAtIndexFunction::OnAnimationComplete,
this))) {
return RespondNow(WithArguments(false));
}
return RespondLater();
}
void AutotestPrivateActivateDeskAtIndexFunction::OnAnimationComplete() {
Respond(WithArguments(true));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateRemoveActiveDeskFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateRemoveActiveDeskFunction::
AutotestPrivateRemoveActiveDeskFunction() = default;
AutotestPrivateRemoveActiveDeskFunction::
~AutotestPrivateRemoveActiveDeskFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateRemoveActiveDeskFunction::Run() {
// Check whether overview mode was active before removing the desk. In case of
// split view, the desk removal may cause overview to end, but what matters is
// whether overview mode was active before.
const bool was_in_overview =
ash::OverviewController::Get()->InOverviewSession();
if (!ash::AutotestDesksApi().RemoveActiveDesk(base::BindOnce(
&AutotestPrivateRemoveActiveDeskFunction::OnAnimationComplete,
this))) {
return RespondNow(WithArguments(false));
}
// In overview, the desk removal animation does not apply, so we should not
// wait for it.
if (was_in_overview) {
return RespondNow(WithArguments(true));
}
return RespondLater();
}
void AutotestPrivateRemoveActiveDeskFunction::OnAnimationComplete() {
Respond(WithArguments(true));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateActivateAdjacentDesksToTargetIndexFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateActivateAdjacentDesksToTargetIndexFunction::
AutotestPrivateActivateAdjacentDesksToTargetIndexFunction() = default;
AutotestPrivateActivateAdjacentDesksToTargetIndexFunction::
~AutotestPrivateActivateAdjacentDesksToTargetIndexFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateActivateAdjacentDesksToTargetIndexFunction::Run() {
std::optional<
api::autotest_private::ActivateAdjacentDesksToTargetIndex::Params>
params = api::autotest_private::ActivateAdjacentDesksToTargetIndex::
Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
if (!ash::AutotestDesksApi().ActivateAdjacentDesksToTargetIndex(
params->index,
base::BindOnce(
&AutotestPrivateActivateAdjacentDesksToTargetIndexFunction::
OnAnimationComplete,
this))) {
return RespondNow(WithArguments(false));
}
return RespondLater();
}
void AutotestPrivateActivateAdjacentDesksToTargetIndexFunction::
OnAnimationComplete() {
Respond(WithArguments(true));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetDeskCountFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetDeskCountFunction::AutotestPrivateGetDeskCountFunction() =
default;
AutotestPrivateGetDeskCountFunction::~AutotestPrivateGetDeskCountFunction() =
default;
ExtensionFunction::ResponseAction AutotestPrivateGetDeskCountFunction::Run() {
return RespondNow(
WithArguments(ash::AutotestDesksApi().GetDesksInfo().num_desks));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetDesksInfoFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetDesksInfoFunction::AutotestPrivateGetDesksInfoFunction() =
default;
AutotestPrivateGetDesksInfoFunction::~AutotestPrivateGetDesksInfoFunction() =
default;
ExtensionFunction::ResponseAction AutotestPrivateGetDesksInfoFunction::Run() {
ash::AutotestDesksApi::DesksInfo desks_info =
ash::AutotestDesksApi().GetDesksInfo();
base::Value::Dict result;
result.Set("activeDeskIndex", desks_info.active_desk_index);
result.Set("numDesks", desks_info.num_desks);
result.Set("isAnimating", desks_info.is_animating);
base::Value::List desk_containers;
for (std::string& desk_container : desks_info.desk_containers) {
desk_containers.Append(std::move(desk_container));
}
result.Set("deskContainers", std::move(desk_containers));
return RespondNow(WithArguments(std::move(result)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateMouseClickFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateMouseClickFunction::AutotestPrivateMouseClickFunction() =
default;
AutotestPrivateMouseClickFunction::~AutotestPrivateMouseClickFunction() =
default;
ExtensionFunction::ResponseAction AutotestPrivateMouseClickFunction::Run() {
std::optional<api::autotest_private::MouseClick::Params> params =
api::autotest_private::MouseClick::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
auto* env = aura::Env::GetInstance();
if (env->mouse_button_flags() != 0) {
return RespondNow(Error(base::StringPrintf("Already pressed; flags %d",
env->mouse_button_flags())));
}
int64_t display_id = ash::Shell::Get()->cursor_manager()->GetDisplay().id();
auto* root_window = ash::Shell::GetRootWindowForDisplayId(display_id);
if (!root_window) {
return RespondNow(Error("Failed to find the root window"));
}
gfx::PointF location_in_host(env->last_mouse_location().x(),
env->last_mouse_location().y());
wm::ConvertPointFromScreen(root_window, &location_in_host);
ConvertPointToHost(root_window, &location_in_host);
int flags = GetMouseEventFlags(params->button);
event_generator_ = std::make_unique<EventGenerator>(
root_window->GetHost(),
base::BindOnce(&AutotestPrivateMouseClickFunction::Respond, this,
NoArguments()));
event_generator_->ScheduleMouseEvent(ui::EventType::kMousePressed,
location_in_host, flags);
event_generator_->ScheduleMouseEvent(ui::EventType::kMouseReleased,
location_in_host, flags);
event_generator_->Run();
return RespondLater();
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateMousePressFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateMousePressFunction::AutotestPrivateMousePressFunction() =
default;
AutotestPrivateMousePressFunction::~AutotestPrivateMousePressFunction() =
default;
ExtensionFunction::ResponseAction AutotestPrivateMousePressFunction::Run() {
std::optional<api::autotest_private::MousePress::Params> params =
api::autotest_private::MousePress::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
auto* env = aura::Env::GetInstance();
int input_flags = GetMouseEventFlags(params->button);
if ((input_flags | env->mouse_button_flags()) == env->mouse_button_flags()) {
return RespondNow(NoArguments());
}
int64_t display_id = ash::Shell::Get()->cursor_manager()->GetDisplay().id();
auto* root_window = ash::Shell::GetRootWindowForDisplayId(display_id);
if (!root_window) {
return RespondNow(Error("Failed to find the root window"));
}
gfx::PointF location_in_host(env->last_mouse_location().x(),
env->last_mouse_location().y());
wm::ConvertPointFromScreen(root_window, &location_in_host);
ConvertPointToHost(root_window, &location_in_host);
event_generator_ = std::make_unique<EventGenerator>(
root_window->GetHost(),
base::BindOnce(&AutotestPrivateMousePressFunction::Respond, this,
NoArguments()));
event_generator_->ScheduleMouseEvent(ui::EventType::kMousePressed,
location_in_host, input_flags);
event_generator_->Run();
return RespondLater();
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateMouseReleaseFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateMouseReleaseFunction::AutotestPrivateMouseReleaseFunction() =
default;
AutotestPrivateMouseReleaseFunction::~AutotestPrivateMouseReleaseFunction() =
default;
ExtensionFunction::ResponseAction AutotestPrivateMouseReleaseFunction::Run() {
std::optional<api::autotest_private::MouseRelease::Params> params =
api::autotest_private::MouseRelease::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
auto* env = aura::Env::GetInstance();
int input_flags = GetMouseEventFlags(params->button);
if ((env->mouse_button_flags() & (~input_flags)) ==
env->mouse_button_flags()) {
return RespondNow(NoArguments());
}
int64_t display_id = ash::Shell::Get()->cursor_manager()->GetDisplay().id();
auto* root_window = ash::Shell::GetRootWindowForDisplayId(display_id);
if (!root_window) {
return RespondNow(Error("Failed to find the root window"));
}
gfx::PointF location_in_host(env->last_mouse_location().x(),
env->last_mouse_location().y());
wm::ConvertPointFromScreen(root_window, &location_in_host);
ConvertPointToHost(root_window, &location_in_host);
event_generator_ = std::make_unique<EventGenerator>(
root_window->GetHost(),
base::BindOnce(&AutotestPrivateMouseReleaseFunction::Respond, this,
NoArguments()));
event_generator_->ScheduleMouseEvent(ui::EventType::kMouseReleased,
location_in_host, input_flags);
event_generator_->Run();
return RespondLater();
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateMouseMoveFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateMouseMoveFunction::AutotestPrivateMouseMoveFunction() = default;
AutotestPrivateMouseMoveFunction::~AutotestPrivateMouseMoveFunction() = default;
ExtensionFunction::ResponseAction AutotestPrivateMouseMoveFunction::Run() {
std::optional<api::autotest_private::MouseMove::Params> params =
api::autotest_private::MouseMove::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
int64_t display_id = ash::Shell::Get()->cursor_manager()->GetDisplay().id();
auto* root_window = ash::Shell::GetRootWindowForDisplayId(display_id);
if (!root_window) {
return RespondNow(Error("Failed to find the root window"));
}
gfx::Point location_in_screen(params->location.x, params->location.y);
auto* env = aura::Env::GetInstance();
const gfx::Point last_mouse_location(env->last_mouse_location());
if (last_mouse_location == location_in_screen) {
return RespondNow(NoArguments());
}
event_generator_ = std::make_unique<EventGenerator>(
root_window->GetHost(),
base::BindOnce(&AutotestPrivateMouseMoveFunction::Respond, this,
NoArguments()));
int64_t steps = std::max(
base::ClampFloor<int64_t>(params->duration_in_ms /
event_generator_->interval().InMillisecondsF()),
static_cast<int64_t>(1));
int flags = env->mouse_button_flags();
for (int64_t i = 1; i <= steps; ++i) {
double progress = static_cast<double>(i) / static_cast<double>(steps);
gfx::PointF point(
gfx::Tween::FloatValueBetween(progress, last_mouse_location.x(),
location_in_screen.x()),
gfx::Tween::FloatValueBetween(progress, last_mouse_location.y(),
location_in_screen.y()));
event_generator_->ScheduleMouseEvent(ui::EventType::kMouseMoved, point,
flags);
}
event_generator_->Run();
return RespondLater();
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetMetricsEnabledFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetMetricsEnabledFunction::
AutotestPrivateSetMetricsEnabledFunction() = default;
AutotestPrivateSetMetricsEnabledFunction::
~AutotestPrivateSetMetricsEnabledFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetMetricsEnabledFunction::Run() {
std::optional<api::autotest_private::SetMetricsEnabled::Params> params =
api::autotest_private::SetMetricsEnabled::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
VLOG(1) << "AutotestPrivateSetMetricsEnabledFunction " << std::boolalpha
<< params->enabled;
target_value_ = params->enabled;
Profile* profile = Profile::FromBrowserContext(browser_context());
bool value;
if (ash::CrosSettings::Get()->GetBoolean(ash::kStatsReportingPref, &value) &&
value == target_value_) {
VLOG(1) << "Value at target; returning early";
return RespondNow(NoArguments());
}
ash::StatsReportingController* stats_reporting_controller =
ash::StatsReportingController::Get();
stats_reporting_controller->SetOnDeviceSettingsStoredCallBack(base::BindOnce(
&AutotestPrivateSetMetricsEnabledFunction::OnDeviceSettingsStored, this));
// Set the preference to indicate metrics are enabled/disabled.
stats_reporting_controller->SetEnabled(profile, target_value_);
return RespondLater();
}
void AutotestPrivateSetMetricsEnabledFunction::OnDeviceSettingsStored() {
bool actual;
if (!ash::CrosSettings::Get()->GetBoolean(ash::kStatsReportingPref,
&actual)) {
NOTREACHED_IN_MIGRATION() << "AutotestPrivateSetMetricsEnabledFunction: "
<< "kStatsReportingPref should be set";
Respond(Error(base::StrCat({"Failed to set metrics consent: ",
ash::kStatsReportingPref, " is not set."})));
return;
}
VLOG(1) << "AutotestPrivateSetMetricsEnabledFunction: actual: "
<< std::boolalpha << actual << " and expected: " << std::boolalpha
<< target_value_;
if (actual == target_value_) {
Respond(NoArguments());
} else {
Respond(Error(base::StrCat(
{"Failed to set metrics consent: ", ash::kStatsReportingPref,
" has wrong value."})));
}
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetArcTouchModeFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetArcTouchModeFunction::
AutotestPrivateSetArcTouchModeFunction() = default;
AutotestPrivateSetArcTouchModeFunction::
~AutotestPrivateSetArcTouchModeFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetArcTouchModeFunction::Run() {
std::optional<api::autotest_private::SetArcTouchMode::Params> params =
api::autotest_private::SetArcTouchMode::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateSetArcTouchModeFunction " << params->enabled;
if (!arc::SetTouchMode(params->enabled)) {
return RespondNow(Error("Could not send intent to ARC."));
}
return RespondNow(NoArguments());
}
////////////////////////////////////////////////////////////////////////////////
// AutotestPrivatePinShelfIconFunction
////////////////////////////////////////////////////////////////////////////////
AutotestPrivatePinShelfIconFunction::AutotestPrivatePinShelfIconFunction() =
default;
AutotestPrivatePinShelfIconFunction::~AutotestPrivatePinShelfIconFunction() =
default;
ExtensionFunction::ResponseAction AutotestPrivatePinShelfIconFunction::Run() {
std::optional<api::autotest_private::PinShelfIcon::Params> params =
api::autotest_private::PinShelfIcon::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivatePinShelfIconFunction " << params->app_id;
ChromeShelfController* const controller = ChromeShelfController::instance();
if (!controller) {
return RespondNow(Error("Controller not available"));
}
PinAppWithIDToShelf(params->app_id);
return RespondNow(NoArguments());
}
////////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetShelfIconPinFunction
////////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetShelfIconPinFunction::
AutotestPrivateSetShelfIconPinFunction() = default;
AutotestPrivateSetShelfIconPinFunction::
~AutotestPrivateSetShelfIconPinFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetShelfIconPinFunction::Run() {
std::optional<api::autotest_private::SetShelfIconPin::Params> params =
api::autotest_private::SetShelfIconPin::Params::Create(args());
ChromeShelfController* const controller = ChromeShelfController::instance();
if (!controller) {
return RespondNow(Error("Controller not available"));
}
const std::vector<api::autotest_private::ShelfIconPinUpdateParam>&
update_params = params->update_params;
// Save the app IDs causing errors.
std::vector<std::string> problematic_app_ids;
for (const auto& update_param : update_params) {
const std::string& app_id = update_param.app_id;
if (!controller->AllowedToSetAppPinState(app_id, update_param.pinned)) {
problematic_app_ids.push_back(app_id);
}
}
if (!problematic_app_ids.empty()) {
return RespondNow(
Error(base::StrCat({"Unable to update pin state: ",
base::JoinString(problematic_app_ids, ",")})));
}
// Save the ids of the apps whose pin states are updated. Note that the apps
// which reach the target pin states before api function execution are not
// included in `updated_apps`.
std::vector<std::string> updated_apps;
for (const auto& update_param : update_params) {
const std::string& app_id = update_param.app_id;
// Already reach the target pin state. No op.
if (update_param.pinned == controller->IsAppPinned(app_id)) {
continue;
}
if (update_param.pinned) {
PinAppWithIDToShelf(app_id);
} else {
UnpinAppWithIDFromShelf(app_id);
}
updated_apps.push_back(app_id);
}
return RespondNow(ArgumentList(
api::autotest_private::SetShelfIconPin::Results::Create(updated_apps)));
}
////////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetScrollableShelfInfoForStateFunction
////////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetScrollableShelfInfoForStateFunction::
AutotestPrivateGetScrollableShelfInfoForStateFunction() = default;
AutotestPrivateGetScrollableShelfInfoForStateFunction::
~AutotestPrivateGetScrollableShelfInfoForStateFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateGetScrollableShelfInfoForStateFunction::Run() {
DVLOG(1) << "AutotestPrivateGetScrollableShelfInfoForStateFunction";
std::optional<api::autotest_private::GetScrollableShelfInfoForState::Params>
params =
api::autotest_private::GetScrollableShelfInfoForState::Params::Create(
args());
ash::ShelfTestApi shelf_test_api;
ash::ShelfState state;
if (params->state.scroll_distance) {
state.scroll_distance = *params->state.scroll_distance;
}
ash::ScrollableShelfInfo fetched_info =
shelf_test_api.GetScrollableShelfInfoForState(state);
api::autotest_private::ScrollableShelfInfo info;
info.main_axis_offset = fetched_info.main_axis_offset;
info.page_offset = fetched_info.page_offset;
info.left_arrow_bounds = ToBoundsDictionary(fetched_info.left_arrow_bounds);
info.right_arrow_bounds = ToBoundsDictionary(fetched_info.right_arrow_bounds);
info.is_animating = fetched_info.is_animating;
info.is_overflow = fetched_info.is_overflow;
if (params->state.scroll_distance) {
info.target_main_axis_offset = fetched_info.target_main_axis_offset;
}
return RespondNow(WithArguments(info.ToValue()));
}
////////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetShelfUIInfoForStateFunction
////////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetShelfUIInfoForStateFunction::
AutotestPrivateGetShelfUIInfoForStateFunction() = default;
AutotestPrivateGetShelfUIInfoForStateFunction::
~AutotestPrivateGetShelfUIInfoForStateFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateGetShelfUIInfoForStateFunction::Run() {
DVLOG(1) << "AutotestPrivateGetShelfUIInfoForStateFunction";
std::optional<api::autotest_private::GetShelfUIInfoForState::Params> params =
api::autotest_private::GetShelfUIInfoForState::Params::Create(args());
ash::ShelfState state;
if (params->state.scroll_distance) {
state.scroll_distance = *params->state.scroll_distance;
}
api::autotest_private::ShelfUIInfo shelf_ui_info;
ash::ShelfTestApi shelf_test_api;
// Fetch scrollable shelf ui information.
{
ash::ScrollableShelfInfo fetched_info =
shelf_test_api.GetScrollableShelfInfoForState(state);
api::autotest_private::ScrollableShelfInfo scrollable_shelf_ui_info;
scrollable_shelf_ui_info.main_axis_offset = fetched_info.main_axis_offset;
scrollable_shelf_ui_info.page_offset = fetched_info.page_offset;
scrollable_shelf_ui_info.left_arrow_bounds =
ToBoundsDictionary(fetched_info.left_arrow_bounds);
scrollable_shelf_ui_info.right_arrow_bounds =
ToBoundsDictionary(fetched_info.right_arrow_bounds);
scrollable_shelf_ui_info.is_animating = fetched_info.is_animating;
scrollable_shelf_ui_info.icons_under_animation =
fetched_info.icons_under_animation;
scrollable_shelf_ui_info.is_overflow = fetched_info.is_overflow;
scrollable_shelf_ui_info.icons_bounds_in_screen =
ToBoundsDictionaryList(fetched_info.icons_bounds_in_screen);
scrollable_shelf_ui_info.is_shelf_widget_animating =
fetched_info.is_shelf_widget_animating;
if (state.scroll_distance) {
scrollable_shelf_ui_info.target_main_axis_offset =
fetched_info.target_main_axis_offset;
}
shelf_ui_info.scrollable_shelf_info = std::move(scrollable_shelf_ui_info);
}
// Fetch hotseat ui information.
{
ash::HotseatInfo hotseat_info = shelf_test_api.GetHotseatInfo();
api::autotest_private::HotseatSwipeDescriptor swipe_up_descriptor;
swipe_up_descriptor.swipe_start_location =
ToLocationDictionary(hotseat_info.swipe_up.swipe_start_location);
swipe_up_descriptor.swipe_end_location =
ToLocationDictionary(hotseat_info.swipe_up.swipe_end_location);
api::autotest_private::HotseatInfo hotseat_ui_info;
hotseat_ui_info.swipe_up = std::move(swipe_up_descriptor);
hotseat_ui_info.is_animating = hotseat_info.is_animating;
hotseat_ui_info.state = GetHotseatState(hotseat_info.hotseat_state);
hotseat_ui_info.is_auto_hidden = hotseat_info.is_auto_hidden;
shelf_ui_info.hotseat_info = std::move(hotseat_ui_info);
}
return RespondNow(WithArguments(shelf_ui_info.ToValue()));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetWindowBoundsFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetWindowBoundsFunction::
AutotestPrivateSetWindowBoundsFunction() = default;
AutotestPrivateSetWindowBoundsFunction::
~AutotestPrivateSetWindowBoundsFunction() = default;
namespace {
base::Value::Dict BuildSetWindowBoundsResult(const gfx::Rect& bounds_in_display,
int64_t display_id) {
base::Value::Dict result;
result.Set("bounds", ToBoundsDictionary(bounds_in_display).ToValue());
result.Set("displayId", base::NumberToString(display_id));
return result;
}
} // namespace
ExtensionFunction::ResponseAction
AutotestPrivateSetWindowBoundsFunction::Run() {
std::optional<api::autotest_private::SetWindowBounds::Params> params =
api::autotest_private::SetWindowBounds::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
aura::Window* window = FindAppWindowById(params->id);
if (!window) {
return RespondNow(Error(
base::StringPrintf("No app window was found : id=%d", params->id)));
}
auto* state = ash::WindowState::Get(window);
if (!state || chromeos::ToWindowShowState(state->GetStateType()) !=
ui::SHOW_STATE_NORMAL) {
return RespondNow(
Error("Cannot set bounds of window not in normal show state."));
}
int64_t display_id;
if (!base::StringToInt64(params->display_id, &display_id)) {
return RespondNow(Error(base::StrCat(
{"Invalid display_id; expected string with numbers only, got ",
params->display_id})));
}
display::Display display;
display::Screen::GetScreen()->GetDisplayWithDisplayId(display_id, &display);
if (!display.is_valid()) {
return RespondNow(
Error("Given display ID does not correspond to a valid display"));
}
auto* root_window = ash::Shell::GetRootWindowForDisplayId(display_id);
if (!root_window) {
return RespondNow(Error("Failed to find the root window"));
}
gfx::Rect to_bounds = ToRect(params->bounds);
if (window->GetBoundsInRootWindow() == to_bounds &&
state->GetDisplay().id() == display_id) {
return RespondNow(
WithArguments(BuildSetWindowBoundsResult(to_bounds, display_id)));
}
window_bounds_observer_ = std::make_unique<WindowBoundsChangeObserver>(
window, to_bounds, display_id,
base::BindOnce(
&AutotestPrivateSetWindowBoundsFunction::WindowBoundsChanged, this));
::wm::ConvertRectToScreen(root_window, &to_bounds);
window->SetBoundsInScreen(to_bounds, display);
return RespondLater();
}
void AutotestPrivateSetWindowBoundsFunction::WindowBoundsChanged(
const gfx::Rect& bounds_in_display,
int64_t display_id,
bool success) {
if (!success) {
Respond(Error(
"The app window was destroyed while waiting for bounds to change!"));
} else {
Respond(WithArguments(
BuildSetWindowBoundsResult(bounds_in_display, display_id)));
}
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateStartSmoothnessTrackingFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateStartSmoothnessTrackingFunction::
~AutotestPrivateStartSmoothnessTrackingFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateStartSmoothnessTrackingFunction::Run() {
auto params =
api::autotest_private::StartSmoothnessTracking::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
int64_t display_id;
if (!GetDisplayIdFromOptionalArg(params->display_id, &display_id)) {
return RespondNow(
Error(base::StrCat({"Invalid display id: ", *params->display_id})));
}
auto* trackers = GetDisplaySmoothnessTrackers();
if (trackers->find(display_id) != trackers->end()) {
return RespondNow(
Error(base::StrCat({"Smoothness already tracked for display: ",
base::NumberToString(display_id)})));
}
base::TimeDelta throughput_interval = kDefaultThroughputInterval;
if (params->throughput_interval_ms) {
throughput_interval = base::Milliseconds(*params->throughput_interval_ms);
}
auto tracker = std::make_unique<DisplaySmoothnessTracker>();
if (!tracker->Start(
display_id, throughput_interval,
base::BindOnce(&ForwardFrameRateDataAndReset, display_id))) {
return RespondNow(Error(base::StrCat(
{"Invalid display_id; no root window found for the display id ",
base::NumberToString(display_id)})));
}
(*trackers)[display_id] = std::move(tracker);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateStopSmoothnessTrackingFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateStopSmoothnessTrackingFunction::
~AutotestPrivateStopSmoothnessTrackingFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateStopSmoothnessTrackingFunction::Run() {
auto params =
api::autotest_private::StopSmoothnessTracking::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
int64_t display_id;
if (!GetDisplayIdFromOptionalArg(params->display_id, &display_id)) {
return RespondNow(
Error(base::StrCat({"Invalid display id: ", *params->display_id})));
}
auto* trackers = GetDisplaySmoothnessTrackers();
auto it = trackers->find(display_id);
if (it == trackers->end()) {
return RespondNow(
Error(base::StrCat({"Smoothness is not tracked for display: ",
base::NumberToString(display_id)})));
}
auto& [_, tracker] = *it;
if (tracker->stopping()) {
return RespondNow(Error(
base::StrCat({"stopSmoothnessTracking already called for display: ",
base::NumberToString(display_id)})));
}
const bool has_error = tracker->has_error();
#if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \
defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \
defined(UNDEFINED_SANITIZER)
// Use a longer report timeout for sanitizers. See http://crbug.com/41491890.
constexpr base::TimeDelta kReportTimeout = base::Seconds(20);
#else
constexpr base::TimeDelta kReportTimeout = base::Seconds(5);
#endif
// DisplaySmoothnessTracker::Stop does not invoke the report callback when
// gpu-process crashes and has no valid data to report. Start a timer to
// handle this case.
timeout_timer_.Start(
FROM_HERE, kReportTimeout,
base::BindOnce(&AutotestPrivateStopSmoothnessTrackingFunction::OnTimeOut,
this, display_id));
if (!tracker->Stop(base::BindOnce(
&AutotestPrivateStopSmoothnessTrackingFunction::OnReportData, this,
tracker->start_time()))) {
timeout_timer_.AbandonAndStop();
trackers->erase(it);
return RespondNow(
Error("No smoothness report, GPU process may have crashed"));
}
// Trigger a repaint after ThroughputTracker::Stop() to generate a frame to
// ensure the tracker report will be sent back.
auto* root_window = ash::Shell::GetRootWindowForDisplayId(display_id);
root_window->GetHost()->compositor()->ScheduleFullRedraw();
if (has_error) {
return RespondNow(Error(base::StrCat(
{"Error happened during smoothness collection for display: ",
base::NumberToString(display_id)})));
}
return did_respond() ? AlreadyResponded() : RespondLater();
}
void AutotestPrivateStopSmoothnessTrackingFunction::OnReportData(
base::TimeTicks start_time,
const cc::FrameSequenceMetrics::CustomReportData& frame_data,
std::vector<int>&& throughput) {
if (did_respond()) {
return;
}
timeout_timer_.AbandonAndStop();
std::vector<int> jank_timestamps; // In milliseconds.
std::vector<int> jank_durations; // In milliseconds.
jank_timestamps.reserve(frame_data.janks.size());
jank_durations.reserve(frame_data.janks.size());
for (auto jank : frame_data.janks) {
jank_timestamps.emplace_back(
(jank.start_time - start_time).InMilliseconds());
jank_durations.emplace_back(jank.duration.InMilliseconds());
}
api::autotest_private::DisplaySmoothnessData result_data;
result_data.frames_expected = frame_data.frames_expected_v3;
result_data.frames_produced =
frame_data.frames_expected_v3 - frame_data.frames_dropped_v3;
result_data.jank_count = frame_data.jank_count_v3;
result_data.throughput = std::move(throughput);
result_data.jank_timestamps = std::move(jank_timestamps);
result_data.jank_durations = std::move(jank_durations);
Respond(ArgumentList(
api::autotest_private::StopSmoothnessTracking::Results::Create(
result_data)));
}
void AutotestPrivateStopSmoothnessTrackingFunction::OnTimeOut(
int64_t display_id) {
if (did_respond()) {
return;
}
// Clean up the non-functional tracker.
auto* trackers = GetDisplaySmoothnessTrackers();
auto it = trackers->find(display_id);
if (it == trackers->end()) {
return;
}
it->second->CancelReport();
trackers->erase(it);
Respond(Error("Smoothness is not available"));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateWaitForAmbientPhotoAnimationFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateWaitForAmbientPhotoAnimationFunction::
AutotestPrivateWaitForAmbientPhotoAnimationFunction() = default;
AutotestPrivateWaitForAmbientPhotoAnimationFunction::
~AutotestPrivateWaitForAmbientPhotoAnimationFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateWaitForAmbientPhotoAnimationFunction::Run() {
std::optional<api::autotest_private::WaitForAmbientPhotoAnimation::Params>
params =
api::autotest_private::WaitForAmbientPhotoAnimation::Params::Create(
args());
EXTENSION_FUNCTION_VALIDATE(params);
// Wait for photo transition animation completed in ambient mode.
ash::AutotestAmbientApi().WaitForPhotoTransitionAnimationCompleted(
params->num_completions, base::Seconds(params->timeout),
/*on_complete=*/
base::BindOnce(&AutotestPrivateWaitForAmbientPhotoAnimationFunction::
OnPhotoTransitionAnimationCompleted,
this),
/*on_timeout=*/
base::BindOnce(
&AutotestPrivateWaitForAmbientPhotoAnimationFunction::Timeout, this));
return did_respond() ? AlreadyResponded() : RespondLater();
}
void AutotestPrivateWaitForAmbientPhotoAnimationFunction::
OnPhotoTransitionAnimationCompleted() {
if (did_respond()) {
return;
}
Respond(NoArguments());
}
void AutotestPrivateWaitForAmbientPhotoAnimationFunction::Timeout() {
if (did_respond()) {
return;
}
Respond(Error("Not enough animations completed before time out."));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateWaitForAmbientVideoFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateWaitForAmbientVideoFunction::
AutotestPrivateWaitForAmbientVideoFunction() = default;
AutotestPrivateWaitForAmbientVideoFunction::
~AutotestPrivateWaitForAmbientVideoFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateWaitForAmbientVideoFunction::Run() {
std::optional<api::autotest_private::WaitForAmbientVideo::Params> params =
api::autotest_private::WaitForAmbientVideo::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
// Wait for video playback to start in ambient mode.
ash::AutotestAmbientApi().WaitForVideoToStart(
base::Seconds(params->timeout),
/*on_complete=*/
base::BindOnce(
&AutotestPrivateWaitForAmbientVideoFunction::RespondWithSuccess,
this),
/*on_error=*/
base::BindOnce(
&AutotestPrivateWaitForAmbientVideoFunction::RespondWithError, this));
return did_respond() ? AlreadyResponded() : RespondLater();
}
void AutotestPrivateWaitForAmbientVideoFunction::RespondWithSuccess() {
Respond(NoArguments());
}
void AutotestPrivateWaitForAmbientVideoFunction::RespondWithError(
std::string error_message) {
Respond(Error(std::move(error_message)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateDisableSwitchAccessDialogFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateDisableSwitchAccessDialogFunction::
AutotestPrivateDisableSwitchAccessDialogFunction() = default;
AutotestPrivateDisableSwitchAccessDialogFunction::
~AutotestPrivateDisableSwitchAccessDialogFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateDisableSwitchAccessDialogFunction::Run() {
auto* accessibility_controller = ash::AccessibilityController::Get();
accessibility_controller
->DisableSwitchAccessDisableConfirmationDialogTesting();
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateDisableAutomationFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateDisableAutomationFunction::
AutotestPrivateDisableAutomationFunction() = default;
AutotestPrivateDisableAutomationFunction::
~AutotestPrivateDisableAutomationFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateDisableAutomationFunction::Run() {
// This disables accessibility for Chrome Views.
AutomationManagerAura::GetInstance()->Disable();
// This disables accessibility for all other accessibility trees including
// ARC++, Ash web contents.
AutomationEventRouter::GetInstance()
->UnregisterAllListenersWithDesktopPermission();
AutomationEventRouter::GetInstance()->NotifyAllAutomationExtensionsGone();
// Finally, this disables accessibility in Lacros.
crosapi::CrosapiManager::Get()
->crosapi_ash()
->automation_ash()
->AllAutomationExtensionsGone();
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateStartThroughputTrackerDataCollectionFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateStartThroughputTrackerDataCollectionFunction::
AutotestPrivateStartThroughputTrackerDataCollectionFunction() = default;
AutotestPrivateStartThroughputTrackerDataCollectionFunction::
~AutotestPrivateStartThroughputTrackerDataCollectionFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateStartThroughputTrackerDataCollectionFunction::Run() {
g_last_start_throughput_data_collection_tick = base::TimeTicks::Now();
ash::metrics_util::StartDataCollection();
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateStopThroughputTrackerDataCollectionFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateStopThroughputTrackerDataCollectionFunction::
AutotestPrivateStopThroughputTrackerDataCollectionFunction() = default;
AutotestPrivateStopThroughputTrackerDataCollectionFunction::
~AutotestPrivateStopThroughputTrackerDataCollectionFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateStopThroughputTrackerDataCollectionFunction::Run() {
auto collected_data = ash::metrics_util::StopDataCollection();
std::vector<api::autotest_private::ThroughputTrackerAnimationData>
result_data;
result_data.reserve(collected_data.size());
for (const auto& data : collected_data) {
api::autotest_private::ThroughputTrackerAnimationData animation_data;
animation_data.start_offset_ms =
(data.start_tick - g_last_start_throughput_data_collection_tick)
.InMilliseconds();
animation_data.stop_offset_ms =
(data.stop_tick - g_last_start_throughput_data_collection_tick)
.InMilliseconds();
animation_data.frames_expected = data.smoothness_data.frames_expected_v3;
animation_data.frames_produced = data.smoothness_data.frames_expected_v3 -
data.smoothness_data.frames_dropped_v3;
animation_data.jank_count = data.smoothness_data.jank_count_v3;
result_data.emplace_back(std::move(animation_data));
}
return RespondNow(
ArgumentList(api::autotest_private::StopThroughputTrackerDataCollection::
Results::Create(result_data)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetThroughputTrackerDataFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetThroughputTrackerDataFunction::
AutotestPrivateGetThroughputTrackerDataFunction() = default;
AutotestPrivateGetThroughputTrackerDataFunction::
~AutotestPrivateGetThroughputTrackerDataFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateGetThroughputTrackerDataFunction::Run() {
auto collected_data = ash::metrics_util::GetCollectedData();
std::vector<api::autotest_private::ThroughputTrackerAnimationData>
result_data;
result_data.reserve(collected_data.size());
for (const auto& data : collected_data) {
api::autotest_private::ThroughputTrackerAnimationData animation_data;
animation_data.start_offset_ms =
(data.start_tick - g_last_start_throughput_data_collection_tick)
.InMilliseconds();
animation_data.stop_offset_ms =
(data.stop_tick - g_last_start_throughput_data_collection_tick)
.InMilliseconds();
animation_data.frames_expected = data.smoothness_data.frames_expected_v3;
animation_data.frames_produced = data.smoothness_data.frames_expected_v3 -
data.smoothness_data.frames_dropped_v3;
animation_data.jank_count = data.smoothness_data.jank_count_v3;
result_data.emplace_back(std::move(animation_data));
}
return RespondNow(ArgumentList(
api::autotest_private::GetThroughputTrackerData::Results::Create(
result_data)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetDisplaySmoothnessFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetDisplaySmoothnessFunction::
AutotestPrivateGetDisplaySmoothnessFunction() = default;
AutotestPrivateGetDisplaySmoothnessFunction::
~AutotestPrivateGetDisplaySmoothnessFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateGetDisplaySmoothnessFunction::Run() {
auto params =
api::autotest_private::GetDisplaySmoothness::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
int64_t display_id;
if (!GetDisplayIdFromOptionalArg(params->display_id, &display_id)) {
return RespondNow(
Error(base::StrCat({"Invalid display id: ", *params->display_id})));
}
auto* root_window = ash::Shell::GetRootWindowForDisplayId(display_id);
const uint32_t smoothness =
100 - root_window->GetHost()->compositor()->GetPercentDroppedFrames();
return RespondNow(
ArgumentList(api::autotest_private::GetDisplaySmoothness::Results::Create(
smoothness)));
}
////////////////////////////////////////////////////////////////////////////////
// AutotestPrivateResetHoldingSpaceFunction
////////////////////////////////////////////////////////////////////////////////
AutotestPrivateResetHoldingSpaceFunction::
AutotestPrivateResetHoldingSpaceFunction() = default;
AutotestPrivateResetHoldingSpaceFunction::
~AutotestPrivateResetHoldingSpaceFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateResetHoldingSpaceFunction::Run() {
auto params =
api::autotest_private::ResetHoldingSpace::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
Profile* profile = Profile::FromBrowserContext(browser_context());
ash::HoldingSpaceKeyedService* service =
ash::HoldingSpaceKeyedServiceFactory::GetInstance()->GetService(profile);
if (service == nullptr) {
return RespondNow(Error("Failed to get `HoldingSpaceKeyedService`."));
}
service->RemoveAll();
PrefService* prefs = profile->GetPrefs();
ash::holding_space_prefs::ResetProfilePrefsForTesting(prefs);
if (!ash::holding_space_prefs::MarkTimeOfFirstAvailability(prefs)) {
return RespondNow(
Error("Failed to call `MarkTimeOfFirstAvailability()` after clearing "
"prefs."));
}
if (!params->options || !params->options->mark_time_of_first_add) {
return RespondNow(NoArguments());
}
if (!ash::holding_space_prefs::MarkTimeOfFirstAdd(prefs)) {
return RespondNow(
Error("Failed to call `MarkTimeOfFirstAdd()` after clearing prefs."));
}
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateStartLoginEventRecorderDataCollectionFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateStartLoginEventRecorderDataCollectionFunction::
AutotestPrivateStartLoginEventRecorderDataCollectionFunction() = default;
AutotestPrivateStartLoginEventRecorderDataCollectionFunction::
~AutotestPrivateStartLoginEventRecorderDataCollectionFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateStartLoginEventRecorderDataCollectionFunction::Run() {
ash::LoginEventRecorder::Get()
->PrepareEventCollectionForTesting(); // IN-TEST
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetLoginEventRecorderLoginEventsFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetLoginEventRecorderLoginEventsFunction::
AutotestPrivateGetLoginEventRecorderLoginEventsFunction() = default;
AutotestPrivateGetLoginEventRecorderLoginEventsFunction::
~AutotestPrivateGetLoginEventRecorderLoginEventsFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateGetLoginEventRecorderLoginEventsFunction::Run() {
const auto& collected_data =
ash::LoginEventRecorder::Get()
->GetCollectedLoginEventsForTesting(); // IN-TEST
std::vector<api::autotest_private::LoginEventRecorderData> result_data;
for (const auto& data : collected_data) {
api::autotest_private::LoginEventRecorderData event_data;
event_data.name = data.name();
event_data.microsecnods_since_unix_epoch =
(data.time() - base::TimeTicks::UnixEpoch()).InMicroseconds();
result_data.emplace_back(std::move(event_data));
}
return RespondNow(ArgumentList(
api::autotest_private::GetLoginEventRecorderLoginEvents::Results::Create(
result_data)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateAddLoginEventForTestingFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateAddLoginEventForTestingFunction::
AutotestPrivateAddLoginEventForTestingFunction() = default;
AutotestPrivateAddLoginEventForTestingFunction::
~AutotestPrivateAddLoginEventForTestingFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateAddLoginEventForTestingFunction::Run() {
ash::LoginEventRecorder::Get()->AddLoginTimeMarker(
/*marker_name=*/"AutotestPrivateTestMarker",
/*send_to_uma=*/false,
/*write_to_file=*/false);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateForceAutoThemeModeFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateForceAutoThemeModeFunction::
AutotestPrivateForceAutoThemeModeFunction() = default;
AutotestPrivateForceAutoThemeModeFunction::
~AutotestPrivateForceAutoThemeModeFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateForceAutoThemeModeFunction::Run() {
DVLOG(1) << "AutotestPrivateForceAutoThemeModeFunction";
std::optional<api::autotest_private::ForceAutoThemeMode::Params> params =
api::autotest_private::ForceAutoThemeMode::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
ash::DarkLightModeControllerImpl* dark_light_mode_controller =
ash::Shell::Get()->dark_light_mode_controller();
DCHECK(dark_light_mode_controller);
dark_light_mode_controller->SetAutoScheduleEnabled(false);
dark_light_mode_controller->SetDarkModeEnabledForTest(
params->dark_mode_enabled);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetAccessTokenFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetAccessTokenFunction::AutotestPrivateGetAccessTokenFunction() =
default;
AutotestPrivateGetAccessTokenFunction::
~AutotestPrivateGetAccessTokenFunction() = default;
ExtensionFunction::ResponseAction AutotestPrivateGetAccessTokenFunction::Run() {
// Require a command line switch to avoid crashing on accident.
if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
ash::switches::kGetAccessTokenForTest)) {
return RespondNow(
Error("* switch is not set", ash::switches::kGetAccessTokenForTest));
}
// This API is available only on test images.
base::SysInfo::CrashIfChromeOSNonTestImage();
auto params = api::autotest_private::GetAccessToken::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
timeout_timer_.Start(
FROM_HERE,
base::Milliseconds(params->access_token_params.timeout_ms
? *params->access_token_params.timeout_ms
: 90000),
base::BindOnce(
&AutotestPrivateGetAccessTokenFunction::RespondWithTimeoutError,
this));
Profile* profile = Profile::FromBrowserContext(browser_context());
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile);
OAuth2AccessTokenManager::ScopeSet scopes(
params->access_token_params.scopes.begin(),
params->access_token_params.scopes.end());
access_token_fetcher_ = identity_manager->CreateAccessTokenFetcherForAccount(
identity_manager
->FindExtendedAccountInfoByEmailAddress(
params->access_token_params.email)
.account_id,
/*oauth_consumer_name=*/"cros_autotest_private", scopes,
base::BindOnce(&AutotestPrivateGetAccessTokenFunction::OnAccessToken,
this),
signin::AccessTokenFetcher::Mode::kImmediate);
return RespondLater();
}
void AutotestPrivateGetAccessTokenFunction::RespondWithTimeoutError() {
if (did_respond()) {
return;
}
Respond(Error("Timed out fetching access token"));
access_token_fetcher_.reset();
}
void AutotestPrivateGetAccessTokenFunction::OnAccessToken(
GoogleServiceAuthError error,
signin::AccessTokenInfo token_info) {
access_token_fetcher_.reset();
timeout_timer_.AbandonAndStop();
if (did_respond()) {
return;
}
if (error.state() != GoogleServiceAuthError::NONE) {
Respond(Error("Failed to get access token: *", error.ToString()));
return;
}
base::Value::Dict token_dict;
token_dict.Set("accessToken", token_info.token);
token_dict.Set(
"expirationTimeUnixMs",
base::Int64ToValue((token_info.expiration_time - base::Time::UnixEpoch())
.InMilliseconds()));
Respond(WithArguments(std::move(token_dict)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateIsInputMethodReadyForTestingFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateIsInputMethodReadyForTestingFunction::
AutotestPrivateIsInputMethodReadyForTestingFunction() = default;
AutotestPrivateIsInputMethodReadyForTestingFunction::
~AutotestPrivateIsInputMethodReadyForTestingFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateIsInputMethodReadyForTestingFunction::Run() {
ash::TextInputMethod* engine =
ash::IMEBridge::Get()->GetCurrentEngineHandler();
return RespondNow(
WithArguments(engine && engine->IsReadyForTesting())); // IN-TEST
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateOverrideOrcaResponseForTestingFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateOverrideOrcaResponseForTestingFunction::
AutotestPrivateOverrideOrcaResponseForTestingFunction() = default;
AutotestPrivateOverrideOrcaResponseForTestingFunction::
~AutotestPrivateOverrideOrcaResponseForTestingFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateOverrideOrcaResponseForTestingFunction::Run() {
std::optional<api::autotest_private::OverrideOrcaResponseForTesting::Params>
params =
api::autotest_private::OverrideOrcaResponseForTesting::Params::Create(
args());
EXTENSION_FUNCTION_VALIDATE(params);
ash::input_method::EditorMediator* editor_mediator =
ash::input_method::EditorMediatorFactory::GetInstance()->GetForProfile(
ash::ProfileHelper::Get()->GetProfileByUser(
user_manager::UserManager::Get()->GetActiveUser()));
return RespondNow(
WithArguments(editor_mediator->SetTextQueryProviderResponseForTesting(
params->array.responses))); // IN-TEST
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateMakeFuseboxTempDirFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateMakeFuseboxTempDirFunction::
~AutotestPrivateMakeFuseboxTempDirFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateMakeFuseboxTempDirFunction::Run() {
fusebox::Server* server = fusebox::Server::GetInstance();
if (!server) {
return RespondNow(Error("Fusebox server instance not available"));
}
server->MakeTempDir(base::BindOnce(
&AutotestPrivateMakeFuseboxTempDirFunction::OnMakeTempDir, this));
return RespondLater();
}
void AutotestPrivateMakeFuseboxTempDirFunction::OnMakeTempDir(
const std::string& error_message,
const std::string& fusebox_file_path,
const std::string& underlying_file_path) {
if (!error_message.empty()) {
Respond(Error(error_message));
return;
}
base::Value::Dict dict;
dict.Set("fuseboxFilePath", fusebox_file_path);
dict.Set("underlyingFilePath", underlying_file_path);
Respond(WithArguments(std::move(dict)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateRemoveFuseboxTempDirFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateRemoveFuseboxTempDirFunction::
~AutotestPrivateRemoveFuseboxTempDirFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateRemoveFuseboxTempDirFunction::Run() {
std::optional<api::autotest_private::RemoveFuseboxTempDir::Params> params =
api::autotest_private::RemoveFuseboxTempDir::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
fusebox::Server* server = fusebox::Server::GetInstance();
if (!server) {
return RespondNow(Error("Fusebox server instance not available"));
}
server->RemoveTempDir(params->fusebox_file_path);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateRemoveComponentExtension
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateRemoveComponentExtensionFunction::
~AutotestPrivateRemoveComponentExtensionFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateRemoveComponentExtensionFunction::Run() {
std::optional<api::autotest_private::RemoveComponentExtension::Params>
params = api::autotest_private::RemoveComponentExtension::Params::Create(
args());
EXTENSION_FUNCTION_VALIDATE(params);
extensions::ExtensionService* extension_service =
extensions::ExtensionSystem::Get(browser_context())->extension_service();
extension_service->component_loader()->Remove(params->extension_id);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateStartFrameCountingFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateStartFrameCountingFunction::
AutotestPrivateStartFrameCountingFunction() = default;
AutotestPrivateStartFrameCountingFunction::
~AutotestPrivateStartFrameCountingFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateStartFrameCountingFunction::Run() {
std::optional<api::autotest_private::StartFrameCounting::Params> params =
api::autotest_private::StartFrameCounting::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
if (params->bucket_size_in_seconds <= 0) {
return RespondNow(
Error("Param bucketSizeInSeconds must be greater than 0s"));
}
// "viz.mojom.FrameCountingPerSinkData" uses uint16 to store frame counts.
// Limit the max bucket size so that the max frame count does not go beyond
// uint16 max. 500s is safe even for a 120fps system.
constexpr int kMaxBucketSizeInSeconds = 500;
if (params->bucket_size_in_seconds > kMaxBucketSizeInSeconds) {
return RespondNow(
Error("Param bucketSizeInSeconds must be less than 500s"));
}
aura::Env::GetInstance()
->context_factory()
->GetHostFrameSinkManager()
->GetFrameSinksMetricsRecorderForTest() // IN-TEST
.StartFrameCounting(base::TimeTicks::Now(),
base::Seconds(params->bucket_size_in_seconds));
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateStopFrameCountingFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateStopFrameCountingFunction::
AutotestPrivateStopFrameCountingFunction() = default;
AutotestPrivateStopFrameCountingFunction::
~AutotestPrivateStopFrameCountingFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateStopFrameCountingFunction::Run() {
auto callback = base::BindOnce(
&AutotestPrivateStopFrameCountingFunction::OnDataReceived, this);
aura::Env::GetInstance()
->context_factory()
->GetHostFrameSinkManager()
->GetFrameSinksMetricsRecorderForTest() // IN-TEST
.StopFrameCounting(std::move(callback));
return RespondLater();
}
void AutotestPrivateStopFrameCountingFunction::OnDataReceived(
viz::mojom::FrameCountingDataPtr data_ptr) {
if (!data_ptr || data_ptr->per_sink_data.empty()) {
Respond(Error("No frame counting data"));
return;
}
// The data to fill in buckets where there is no data points in collected
// frame data, i.e. before the frame sink's creation and after the frame
// sink's destruction.
constexpr int kNotAvailable = -1;
// Get the max size of frame sink data. The frame sink data that does not
// have enough data points (e.g. frame sinks destroyed before the end) will
// have kNotAvailable appended at the end.
size_t size = 0;
for (const auto& per_sink_data : data_ptr->per_sink_data) {
const size_t per_sink_data_size =
per_sink_data->start_bucket + per_sink_data->presented_frames.size();
if (per_sink_data_size > size) {
size = per_sink_data_size;
}
}
std::vector<api::autotest_private::FrameCountingPerSinkData> result;
for (const auto& per_sink_data : data_ptr->per_sink_data) {
// Skip frame sinks with no data points.
if (per_sink_data->presented_frames.empty()) {
continue;
}
api::autotest_private::FrameCountingPerSinkData result_per_sink_data;
result_per_sink_data.sink_type =
CompositorFrameSinkTypeToString(per_sink_data->type);
result_per_sink_data.is_root = per_sink_data->is_root;
result_per_sink_data.debug_label = per_sink_data->debug_label;
if (per_sink_data->start_bucket != 0) {
result_per_sink_data.presented_frames.resize(per_sink_data->start_bucket,
kNotAvailable);
}
std::copy(per_sink_data->presented_frames.begin(),
per_sink_data->presented_frames.end(),
std::back_inserter(result_per_sink_data.presented_frames));
if (result_per_sink_data.presented_frames.size() < size) {
result_per_sink_data.presented_frames.resize(size, kNotAvailable);
}
result.emplace_back(std::move(result_per_sink_data));
}
Respond(ArgumentList(
api::autotest_private::StopFrameCounting::Results::Create(result)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateStartOverdrawTrackingFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateStartOverdrawTrackingFunction::
AutotestPrivateStartOverdrawTrackingFunction() = default;
AutotestPrivateStartOverdrawTrackingFunction::
~AutotestPrivateStartOverdrawTrackingFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateStartOverdrawTrackingFunction::Run() {
std::optional<api::autotest_private::StartOverdrawTracking::Params> params =
api::autotest_private::StartOverdrawTracking::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
int64_t target_display_id;
if (!GetDisplayIdFromOptionalArg(params->display_id, &target_display_id)) {
return RespondNow(
Error(base::StrCat({"Invalid displayId: ", *params->display_id})));
}
DVLOG(1) << "AutotestPrivateStopOverdrawTrackingFunction displayId:"
<< target_display_id;
// Validate display id.
bool found_display = false;
for (aura::Window* const window : ash::Shell::GetAllRootWindows()) {
const int64_t display_id =
display::Screen::GetScreen()->GetDisplayNearestWindow(window).id();
if (display_id == target_display_id) {
found_display = true;
}
}
if (!found_display) {
return RespondNow(Error(base::StringPrintf(
"Invalid displayId; no display found for the display id %" PRId64,
target_display_id)));
}
if (params->bucket_size_in_seconds <= 0) {
return RespondNow(
Error("Invalid bucketSizeInSeconds; must be greater than 0s"));
}
const ui::Compositor* compositor =
ash::Shell::GetRootWindowForDisplayId(target_display_id)
->layer()
->GetCompositor();
aura::Env::GetInstance()
->context_factory()
->GetHostFrameSinkManager()
->GetFrameSinksMetricsRecorderForTest() // IN-TEST
.StartOverdrawTracking(compositor->frame_sink_id(),
base::Seconds(params->bucket_size_in_seconds));
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateStopOverdrawTrackingFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateStopOverdrawTrackingFunction::
AutotestPrivateStopOverdrawTrackingFunction() = default;
AutotestPrivateStopOverdrawTrackingFunction::
~AutotestPrivateStopOverdrawTrackingFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateStopOverdrawTrackingFunction::Run() {
std::optional<api::autotest_private::StopOverdrawTracking::Params> params =
api::autotest_private::StopOverdrawTracking::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
int64_t target_display_id;
if (!GetDisplayIdFromOptionalArg(params->display_id, &target_display_id)) {
return RespondNow(
Error(base::StrCat({"Invalid displayId: ", *params->display_id})));
}
DVLOG(1) << "AutotestPrivateStopOverdrawTrackingFunction displayId:"
<< target_display_id;
// Validate display id.
bool found_display = false;
for (aura::Window* const window : ash::Shell::GetAllRootWindows()) {
const int64_t display_id =
display::Screen::GetScreen()->GetDisplayNearestWindow(window).id();
if (display_id == target_display_id) {
found_display = true;
}
}
if (!found_display) {
return RespondNow(Error(base::StringPrintf(
"Invalid displayId; no display found for the display id %" PRId64,
target_display_id)));
}
const ui::Compositor* compositor =
ash::Shell::GetRootWindowForDisplayId(target_display_id)
->layer()
->GetCompositor();
auto callback = base::BindOnce(
&AutotestPrivateStopOverdrawTrackingFunction::OnDataReceived, this);
aura::Env::GetInstance()
->context_factory()
->GetHostFrameSinkManager()
->GetFrameSinksMetricsRecorderForTest() // IN-TEST
.StopOverdrawTracking(compositor->frame_sink_id(), std::move(callback));
return RespondLater();
}
void AutotestPrivateStopOverdrawTrackingFunction::OnDataReceived(
viz::mojom::OverdrawDataPtr data_ptr) {
// Data can be missing if gpu process is restarted in middle of test
// and test scripts still calls `stopOverdrawTracking`.
if (!data_ptr || data_ptr->average_overdraws.empty()) {
Respond(
Error("No overdraw data; maybe forgot to call startOverdrawTracking or "
"no UI changes between start and stop calls"));
return;
}
api::autotest_private::OverdrawData result;
result.average_overdraws.reserve(data_ptr->average_overdraws.size());
std::copy(data_ptr->average_overdraws.begin(),
data_ptr->average_overdraws.end(),
std::back_inserter(result.average_overdraws));
Respond(ArgumentList(
api::autotest_private::StopOverdrawTracking::Results::Create(result)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateBruschettaInstallFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateInstallBruschettaFunction::
AutotestPrivateInstallBruschettaFunction() = default;
AutotestPrivateInstallBruschettaFunction::
~AutotestPrivateInstallBruschettaFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateInstallBruschettaFunction::Run() {
// This API is available only on test images.
base::SysInfo::CrashIfChromeOSNonTestImage();
std::optional<api::autotest_private::InstallBruschetta::Params> params =
api::autotest_private::InstallBruschetta::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
Profile* profile = Profile::FromBrowserContext(browser_context());
BruschettaInstallerView::Show(profile,
bruschetta::MakeBruschettaId(params->vm_name));
auto* view = BruschettaInstallerView::GetActiveViewForTesting();
if (!view) {
return RespondNow(Error("Couldn't open BruschettaInstallerView"));
}
view->set_finish_callback_for_testing(base::BindOnce(
&AutotestPrivateInstallBruschettaFunction::OnInstallerFinish, this));
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&AutotestPrivateInstallBruschettaFunction::ClickAccept, this));
return RespondLater();
}
void AutotestPrivateInstallBruschettaFunction::ClickAccept() {
auto* view = BruschettaInstallerView::GetActiveViewForTesting();
if (view) {
view->Accept();
} else {
Respond(Error("BruschettaInstallerView was closed unexpectedly"));
}
}
void AutotestPrivateInstallBruschettaFunction::OnInstallerFinish(
bruschetta::BruschettaInstallResult result) {
if (result == bruschetta::BruschettaInstallResult::kSuccess) {
Respond(NoArguments());
} else {
Respond(Error(base::UTF16ToUTF8(std::u16string_view(
bruschetta::BruschettaInstallResultString(result)))));
}
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateBruschettaRemoveFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateRemoveBruschettaFunction::
AutotestPrivateRemoveBruschettaFunction() = default;
AutotestPrivateRemoveBruschettaFunction::
~AutotestPrivateRemoveBruschettaFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateRemoveBruschettaFunction::Run() {
// This API is available only on test images.
base::SysInfo::CrashIfChromeOSNonTestImage();
std::optional<api::autotest_private::RemoveBruschetta::Params> params =
api::autotest_private::RemoveBruschetta::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
Profile* profile = Profile::FromBrowserContext(browser_context());
auto* service = bruschetta::BruschettaService::GetForProfile(profile);
if (!service) {
return RespondNow(Error("Couldn't get BruschettaService instance"));
}
service->RemoveVm(
bruschetta::MakeBruschettaId(params->vm_name),
base::BindOnce(&AutotestPrivateRemoveBruschettaFunction::OnRemoveVm,
this));
return RespondLater();
}
void AutotestPrivateRemoveBruschettaFunction::OnRemoveVm(bool success) {
if (success) {
Respond(NoArguments());
} else {
Respond(Error("Failed to uninstall bruschetta"));
}
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateIsFeatureEnabledFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateIsFeatureEnabledFunction::
AutotestPrivateIsFeatureEnabledFunction() = default;
AutotestPrivateIsFeatureEnabledFunction::
~AutotestPrivateIsFeatureEnabledFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateIsFeatureEnabledFunction::Run() {
std::optional<api::autotest_private::IsFeatureEnabled::Params> params =
api::autotest_private::IsFeatureEnabled::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
// base::FeatureList does not allow lookup by string name. Use an allowlist
// of features instead.
static const base::Feature* const kAllowList[] = {
// clang-format off
&ash::features::kFeatureManagementVideoConference,
&ash::features::kSavedDeskUiRevamp,
&chromeos::features::kJelly,
&kDisabledFeatureForTest,
&kEnabledFeatureForTest,
// clang-format on
};
auto* const* it = base::ranges::find(kAllowList, params->feature_name,
&base::Feature::name);
if (it == std::end(kAllowList)) {
std::string error = base::StrCat(
{"feature ", params->feature_name,
" is not on allowlist, see "
"AutotestPrivateIsFeatureEnabledFunction::Run() to update the list"});
return RespondNow(Error(error));
}
bool enabled = base::FeatureList::IsEnabled(**it);
return RespondNow(WithArguments(enabled));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetCurrentInputMethodDescriptorFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetCurrentInputMethodDescriptorFunction::
AutotestPrivateGetCurrentInputMethodDescriptorFunction() = default;
AutotestPrivateGetCurrentInputMethodDescriptorFunction::
~AutotestPrivateGetCurrentInputMethodDescriptorFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateGetCurrentInputMethodDescriptorFunction::Run() {
auto* manager = ash::input_method::InputMethodManager::Get();
ash::input_method::InputMethodDescriptor descriptor =
manager->GetActiveIMEState()->GetCurrentInputMethod();
base::Value::Dict dict;
dict.Set("keyboardLayout", descriptor.keyboard_layout());
return RespondNow(WithArguments(std::move(dict)));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetArcInteractiveStateFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetArcInteractiveStateFunction::
AutotestPrivateSetArcInteractiveStateFunction() = default;
AutotestPrivateSetArcInteractiveStateFunction::
~AutotestPrivateSetArcInteractiveStateFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetArcInteractiveStateFunction::Run() {
std::optional<api::autotest_private::SetArcInteractiveState::Params> params =
api::autotest_private::SetArcInteractiveState::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
arc::ArcServiceManager* arc_service_manager = arc::ArcServiceManager::Get();
if (!arc_service_manager) {
return RespondNow(Error("ARC service manager is not available"));
}
arc::ArcBridgeService* arc_bridge_service =
arc_service_manager->arc_bridge_service();
if (!arc_bridge_service) {
return RespondNow(Error("ARC bridge service is not available"));
}
arc::mojom::PowerInstance* power_instance =
ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service->power(), SetIdleState);
if (!power_instance) {
return RespondNow(Error("ARC power service is not available"));
}
power_instance->SetIdleState(params->enabled
? arc::mojom::IdleState::ACTIVE
: arc::mojom::IdleState::INACTIVE);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateIsFieldTrialActiveFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateIsFieldTrialActiveFunction::
AutotestPrivateIsFieldTrialActiveFunction() = default;
AutotestPrivateIsFieldTrialActiveFunction::
~AutotestPrivateIsFieldTrialActiveFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateIsFieldTrialActiveFunction::Run() {
std::optional<api::autotest_private::IsFieldTrialActive::Params> params =
api::autotest_private::IsFieldTrialActive::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
base::FieldTrial::ActiveGroups active_groups;
base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
bool found = false;
for (base::FieldTrial::ActiveGroup field_trial : active_groups) {
if (field_trial.trial_name == params->trial_name &&
field_trial.group_name == params->group_name) {
found = true;
break;
}
}
return RespondNow(WithArguments(found));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateGetArcWakefulnessModeFunction
//////////////////////////////////////////////////////////////////////////////
AutotestPrivateGetArcWakefulnessModeFunction::
AutotestPrivateGetArcWakefulnessModeFunction() = default;
AutotestPrivateGetArcWakefulnessModeFunction::
~AutotestPrivateGetArcWakefulnessModeFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateGetArcWakefulnessModeFunction::Run() {
arc::ArcServiceManager* arc_service_manager = arc::ArcServiceManager::Get();
if (!arc_service_manager) {
return RespondNow(Error("ARC service manager is not available"));
}
arc::ArcBridgeService* arc_bridge_service =
arc_service_manager->arc_bridge_service();
if (!arc_bridge_service) {
return RespondNow(Error(
"ARC service manager exist, but ARC bridge service is not available"));
}
arc::mojom::PowerInstance* power_instance =
ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service->power(), SetIdleState);
if (!power_instance) {
return RespondNow(
Error("ARC bridge exist, but ARC power service is not available"));
}
power_instance->GetWakefulnessMode(
base::BindOnce(&AutotestPrivateGetArcWakefulnessModeFunction::
OnGetWakefulnessStateRespond,
this));
return RespondLater();
}
void AutotestPrivateGetArcWakefulnessModeFunction::OnGetWakefulnessStateRespond(
arc::mojom::WakefulnessMode mode) {
return Respond(
WithArguments(api::autotest_private::ToString(GetWakefulnessMode(mode))));
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateSetDeviceLanguageFunction
///////////////////////////////////////////////////////////////////////////////
AutotestPrivateSetDeviceLanguageFunction::
AutotestPrivateSetDeviceLanguageFunction() = default;
AutotestPrivateSetDeviceLanguageFunction::
~AutotestPrivateSetDeviceLanguageFunction() = default;
ExtensionFunction::ResponseAction
AutotestPrivateSetDeviceLanguageFunction::Run() {
std::optional<api::autotest_private::SetDeviceLanguage::Params> params =
api::autotest_private::SetDeviceLanguage::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
DVLOG(1) << "AutotestPrivateSetDeviceLanguageFunction " << params->locale;
Profile* const profile = Profile::FromBrowserContext(browser_context());
// Note that this only change the prefs, a restart would be required for the
// change to take effect.
profile->ChangeAppLocale(params->locale,
Profile::APP_LOCALE_CHANGED_VIA_SETTINGS);
return RespondNow(NoArguments());
}
///////////////////////////////////////////////////////////////////////////////
// AutotestPrivateAPI
///////////////////////////////////////////////////////////////////////////////
static base::LazyInstance<BrowserContextKeyedAPIFactory<AutotestPrivateAPI>>::
DestructorAtExit g_autotest_private_api_factory = LAZY_INSTANCE_INITIALIZER;
// static
BrowserContextKeyedAPIFactory<AutotestPrivateAPI>*
AutotestPrivateAPI::GetFactoryInstance() {
return g_autotest_private_api_factory.Pointer();
}
template <>
KeyedService*
BrowserContextKeyedAPIFactory<AutotestPrivateAPI>::BuildServiceInstanceFor(
content::BrowserContext* context) const {
return new AutotestPrivateAPI(context);
}
AutotestPrivateAPI::AutotestPrivateAPI(content::BrowserContext* context)
: browser_context_(context), test_mode_(false) {
clipboard_observation_.Observe(ui::ClipboardMonitor::GetInstance());
}
AutotestPrivateAPI::~AutotestPrivateAPI() = default;
void AutotestPrivateAPI::OnClipboardDataChanged() {
EventRouter* event_router = EventRouter::Get(browser_context_);
if (!event_router) {
return;
}
std::unique_ptr<Event> event(
new Event(events::AUTOTESTPRIVATE_ON_CLIPBOARD_DATA_CHANGED,
api::autotest_private::OnClipboardDataChanged::kEventName,
base::Value::List()));
event_router->BroadcastEvent(std::move(event));
}
} // namespace extensions