| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/chromeos/extensions/file_manager/event_router.h" |
| |
| #include <stddef.h> |
| |
| #include <memory> |
| #include <set> |
| #include <utility> |
| |
| #include "ash/public/cpp/tablet_mode.h" |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/containers/contains.h" |
| #include "base/files/file_util.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/values.h" |
| #include "chrome/browser/app_mode/app_mode_utils.h" |
| #include "chrome/browser/ash/arc/arc_util.h" |
| #include "chrome/browser/ash/crostini/crostini_pref_names.h" |
| #include "chrome/browser/ash/drive/drive_integration_service.h" |
| #include "chrome/browser/ash/drive/file_system_util.h" |
| #include "chrome/browser/ash/file_manager/app_id.h" |
| #include "chrome/browser/ash/file_manager/fileapi_util.h" |
| #include "chrome/browser/ash/file_manager/open_util.h" |
| #include "chrome/browser/ash/file_manager/path_util.h" |
| #include "chrome/browser/ash/file_manager/volume_manager.h" |
| #include "chrome/browser/ash/login/lock/screen_locker.h" |
| #include "chrome/browser/ash/login/ui/login_display_host.h" |
| #include "chrome/browser/ash/plugin_vm/plugin_vm_features.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/chromeos/extensions/file_manager/private_api_util.h" |
| #include "chrome/browser/extensions/api/file_system/chrome_file_system_delegate.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/extension_util.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/pref_names.h" |
| #include "chromeos/components/drivefs/drivefs_host.h" |
| #include "chromeos/dbus/power/power_manager_client.h" |
| #include "chromeos/disks/disk.h" |
| #include "chromeos/login/login_state/login_state.h" |
| #include "components/arc/arc_prefs.h" |
| #include "components/arc/intent_helper/arc_intent_helper_bridge.h" |
| #include "components/drive/drive_pref_names.h" |
| #include "components/prefs/pref_change_registrar.h" |
| #include "components/prefs/pref_service.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/network_service_instance.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "extensions/browser/event_router.h" |
| #include "extensions/browser/extension_host.h" |
| #include "extensions/browser/extension_prefs.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "storage/browser/file_system/external_mount_points.h" |
| #include "storage/common/file_system/file_system_types.h" |
| #include "storage/common/file_system/file_system_util.h" |
| |
| using chromeos::disks::Disk; |
| using chromeos::disks::DiskMountManager; |
| using content::BrowserThread; |
| using drive::DriveIntegrationService; |
| using drive::DriveIntegrationServiceFactory; |
| using file_manager::util::EntryDefinition; |
| using file_manager::util::FileDefinition; |
| |
| namespace file_manager_private = extensions::api::file_manager_private; |
| |
| namespace file_manager { |
| namespace { |
| |
| // Frequency of sending onFileTransferUpdated. |
| const int64_t kProgressEventFrequencyInMilliseconds = 1000; |
| |
| // Checks if the Recovery Tool is running. This is a temporary solution. |
| // TODO(mtomasz): Replace with crbug.com/341902 solution. |
| bool IsRecoveryToolRunning(Profile* profile) { |
| extensions::ExtensionPrefs* extension_prefs = |
| extensions::ExtensionPrefs::Get(profile); |
| if (!extension_prefs) |
| return false; |
| |
| const std::string kRecoveryToolIds[] = { |
| "kkebgepbbgbcmghedmmdfcbdcodlkngh", // Recovery tool staging |
| "jndclpdbaamdhonoechobihbbiimdgai" // Recovery tool prod |
| }; |
| |
| for (size_t i = 0; i < base::size(kRecoveryToolIds); ++i) { |
| const std::string extension_id = kRecoveryToolIds[i]; |
| if (extension_prefs->IsExtensionRunning(extension_id)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // Sends an event named |event_name| with arguments |event_args| to extensions. |
| void BroadcastEvent(Profile* profile, |
| extensions::events::HistogramValue histogram_value, |
| const std::string& event_name, |
| std::vector<base::Value> event_args) { |
| extensions::EventRouter::Get(profile)->BroadcastEvent( |
| std::make_unique<extensions::Event>(histogram_value, event_name, |
| std::move(event_args))); |
| } |
| |
| // Sends an event named |event_name| with arguments |event_args| to an extension |
| // of |extention_id|. |
| void DispatchEventToExtension( |
| Profile* profile, |
| const std::string& extension_id, |
| extensions::events::HistogramValue histogram_value, |
| const std::string& event_name, |
| std::vector<base::Value> event_args) { |
| extensions::EventRouter::Get(profile)->DispatchEventToExtension( |
| extension_id, std::make_unique<extensions::Event>( |
| histogram_value, event_name, std::move(event_args))); |
| } |
| |
| file_manager_private::MountCompletedStatus |
| MountErrorToMountCompletedStatus(chromeos::MountError error) { |
| switch (error) { |
| case chromeos::MOUNT_ERROR_NONE: |
| return file_manager_private::MOUNT_COMPLETED_STATUS_SUCCESS; |
| case chromeos::MOUNT_ERROR_UNKNOWN: |
| return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_UNKNOWN; |
| case chromeos::MOUNT_ERROR_INTERNAL: |
| return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_INTERNAL; |
| case chromeos::MOUNT_ERROR_INVALID_ARGUMENT: |
| return file_manager_private:: |
| MOUNT_COMPLETED_STATUS_ERROR_INVALID_ARGUMENT; |
| case chromeos::MOUNT_ERROR_INVALID_PATH: |
| return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_INVALID_PATH; |
| case chromeos::MOUNT_ERROR_PATH_ALREADY_MOUNTED: |
| return file_manager_private:: |
| MOUNT_COMPLETED_STATUS_ERROR_PATH_ALREADY_MOUNTED; |
| case chromeos::MOUNT_ERROR_PATH_NOT_MOUNTED: |
| return file_manager_private:: |
| MOUNT_COMPLETED_STATUS_ERROR_PATH_NOT_MOUNTED; |
| case chromeos::MOUNT_ERROR_DIRECTORY_CREATION_FAILED: |
| return file_manager_private:: |
| MOUNT_COMPLETED_STATUS_ERROR_DIRECTORY_CREATION_FAILED; |
| case chromeos::MOUNT_ERROR_INVALID_MOUNT_OPTIONS: |
| return file_manager_private:: |
| MOUNT_COMPLETED_STATUS_ERROR_INVALID_MOUNT_OPTIONS; |
| case chromeos::MOUNT_ERROR_INVALID_UNMOUNT_OPTIONS: |
| return file_manager_private:: |
| MOUNT_COMPLETED_STATUS_ERROR_INVALID_UNMOUNT_OPTIONS; |
| case chromeos::MOUNT_ERROR_INSUFFICIENT_PERMISSIONS: |
| return file_manager_private:: |
| MOUNT_COMPLETED_STATUS_ERROR_INSUFFICIENT_PERMISSIONS; |
| case chromeos::MOUNT_ERROR_MOUNT_PROGRAM_NOT_FOUND: |
| return file_manager_private:: |
| MOUNT_COMPLETED_STATUS_ERROR_MOUNT_PROGRAM_NOT_FOUND; |
| case chromeos::MOUNT_ERROR_MOUNT_PROGRAM_FAILED: |
| return file_manager_private:: |
| MOUNT_COMPLETED_STATUS_ERROR_MOUNT_PROGRAM_FAILED; |
| case chromeos::MOUNT_ERROR_INVALID_DEVICE_PATH: |
| return file_manager_private:: |
| MOUNT_COMPLETED_STATUS_ERROR_INVALID_DEVICE_PATH; |
| case chromeos::MOUNT_ERROR_UNKNOWN_FILESYSTEM: |
| return file_manager_private:: |
| MOUNT_COMPLETED_STATUS_ERROR_UNKNOWN_FILESYSTEM; |
| case chromeos::MOUNT_ERROR_UNSUPPORTED_FILESYSTEM: |
| return file_manager_private:: |
| MOUNT_COMPLETED_STATUS_ERROR_UNSUPPORTED_FILESYSTEM; |
| case chromeos::MOUNT_ERROR_INVALID_ARCHIVE: |
| return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_INVALID_ARCHIVE; |
| case chromeos::MOUNT_ERROR_NEED_PASSWORD: |
| return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_NEED_PASSWORD; |
| // Not a real error. |
| case chromeos::MOUNT_ERROR_COUNT: |
| NOTREACHED(); |
| } |
| NOTREACHED(); |
| return file_manager_private::MOUNT_COMPLETED_STATUS_NONE; |
| } |
| |
| file_manager_private::CopyOrMoveProgressStatusType |
| CopyOrMoveProgressTypeToCopyOrMoveProgressStatusType( |
| storage::FileSystemOperation::CopyOrMoveProgressType type) { |
| switch (type) { |
| case storage::FileSystemOperation::CopyOrMoveProgressType::kBegin: |
| return file_manager_private::COPY_OR_MOVE_PROGRESS_STATUS_TYPE_BEGIN; |
| case storage::FileSystemOperation::CopyOrMoveProgressType::kProgress: |
| return file_manager_private::COPY_OR_MOVE_PROGRESS_STATUS_TYPE_PROGRESS; |
| case storage::FileSystemOperation::CopyOrMoveProgressType::kEndCopy: |
| return file_manager_private::COPY_OR_MOVE_PROGRESS_STATUS_TYPE_END_COPY; |
| case storage::FileSystemOperation::CopyOrMoveProgressType::kEndMove: |
| return file_manager_private::COPY_OR_MOVE_PROGRESS_STATUS_TYPE_END_MOVE; |
| case storage::FileSystemOperation::CopyOrMoveProgressType::kEndRemoveSource: |
| return file_manager_private:: |
| COPY_OR_MOVE_PROGRESS_STATUS_TYPE_END_REMOVE_SOURCE; |
| case storage::FileSystemOperation::CopyOrMoveProgressType::kError: |
| return file_manager_private::COPY_OR_MOVE_PROGRESS_STATUS_TYPE_ERROR; |
| } |
| NOTREACHED(); |
| return file_manager_private::COPY_OR_MOVE_PROGRESS_STATUS_TYPE_NONE; |
| } |
| |
| std::string FileErrorToErrorName(base::File::Error error_code) { |
| namespace js = extensions::api::file_manager_private; |
| switch (error_code) { |
| case base::File::FILE_ERROR_NOT_FOUND: |
| return "NotFoundError"; |
| case base::File::FILE_ERROR_INVALID_OPERATION: |
| case base::File::FILE_ERROR_EXISTS: |
| case base::File::FILE_ERROR_NOT_EMPTY: |
| return "InvalidModificationError"; |
| case base::File::FILE_ERROR_NOT_A_DIRECTORY: |
| case base::File::FILE_ERROR_NOT_A_FILE: |
| return "TypeMismatchError"; |
| case base::File::FILE_ERROR_ACCESS_DENIED: |
| return "NoModificationAllowedError"; |
| case base::File::FILE_ERROR_FAILED: |
| return "InvalidStateError"; |
| case base::File::FILE_ERROR_ABORT: |
| return "AbortError"; |
| case base::File::FILE_ERROR_SECURITY: |
| return "SecurityError"; |
| case base::File::FILE_ERROR_NO_SPACE: |
| return "QuotaExceededError"; |
| case base::File::FILE_ERROR_INVALID_URL: |
| return "EncodingError"; |
| default: |
| return "InvalidModificationError"; |
| } |
| } |
| |
| // Checks if we should send a progress event or not according to the |
| // |last_time| of sending an event. If |always| is true, the function always |
| // returns true. If the function returns true, the function also updates |
| // |last_time|. |
| bool ShouldSendProgressEvent(bool always, base::Time* last_time) { |
| const base::Time now = base::Time::Now(); |
| const int64_t delta = (now - *last_time).InMilliseconds(); |
| // delta < 0 may rarely happen if system clock is synced and rewinded. |
| // To be conservative, we don't skip in that case. |
| if (!always && 0 <= delta && delta < kProgressEventFrequencyInMilliseconds) { |
| return false; |
| } else { |
| *last_time = now; |
| return true; |
| } |
| } |
| |
| // Obtains whether the Files app should handle the volume or not. |
| bool ShouldShowNotificationForVolume( |
| Profile* profile, |
| const DeviceEventRouter& device_event_router, |
| const Volume& volume) { |
| if (volume.type() != VOLUME_TYPE_MTP && |
| volume.type() != VOLUME_TYPE_REMOVABLE_DISK_PARTITION) { |
| return false; |
| } |
| |
| if (device_event_router.is_resuming() || device_event_router.is_starting_up()) |
| return false; |
| |
| // Do not attempt to open File Manager while the login is in progress or |
| // the screen is locked or running in kiosk app mode and make sure the file |
| // manager is opened only for the active user. |
| if (ash::LoginDisplayHost::default_host() || |
| ash::ScreenLocker::default_screen_locker() || |
| chrome::IsRunningInForcedAppMode() || |
| profile != ProfileManager::GetActiveUserProfile()) { |
| return false; |
| } |
| |
| // Do not pop-up the File Manager, if the recovery tool is running. |
| if (IsRecoveryToolRunning(profile)) |
| return false; |
| |
| // If the disable-default-apps flag is on, the Files app is not opened |
| // automatically on device mount not to obstruct the manual test. |
| if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kDisablePreinstalledApps)) { |
| return false; |
| } |
| |
| if (volume.type() == VOLUME_TYPE_REMOVABLE_DISK_PARTITION) { |
| const Disk* disk = DiskMountManager::GetInstance()->FindDiskBySourcePath( |
| volume.source_path().AsUTF8Unsafe()); |
| if (disk) { |
| // We suppress notifications about HP Elite USB-C Dock's internal storage. |
| // chrome-os-partner:58309. |
| // TODO(fukino): Remove this workaround when the root cause is fixed. |
| if (disk->vendor_id() == "0ea0" && disk->product_id() == "2272") { |
| return false; |
| } |
| // Suppress notifications for this disk if it has been mounted before. |
| // This is to avoid duplicate notifications for operations that require a |
| // remount of the disk (e.g. format or rename). |
| if (!disk->is_first_mount()) { |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| std::set<std::string> GetEventListenerExtensionIds( |
| Profile* profile, |
| const std::string& event_name) { |
| const extensions::EventListenerMap::ListenerList& listeners = |
| extensions::EventRouter::Get(profile) |
| ->listeners() |
| .GetEventListenersByName(event_name); |
| std::set<std::string> extension_ids; |
| for (const auto& listener : listeners) { |
| extension_ids.insert(listener->extension_id()); |
| } |
| return extension_ids; |
| } |
| |
| // Sub-part of the event router for handling device events. |
| class DeviceEventRouterImpl : public DeviceEventRouter { |
| public: |
| DeviceEventRouterImpl(SystemNotificationManager* notification_manager, |
| Profile* profile) |
| : DeviceEventRouter(notification_manager), profile_(profile) {} |
| |
| // DeviceEventRouter overrides. |
| void OnDeviceEvent(file_manager_private::DeviceEventType type, |
| const std::string& device_path, |
| const std::string& device_label) override { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| file_manager_private::DeviceEvent event; |
| event.type = type; |
| event.device_path = device_path; |
| event.device_label = device_label; |
| |
| BroadcastEvent(profile_, |
| extensions::events::FILE_MANAGER_PRIVATE_ON_DEVICE_CHANGED, |
| file_manager_private::OnDeviceChanged::kEventName, |
| file_manager_private::OnDeviceChanged::Create(event)); |
| |
| system_notification_manager()->HandleDeviceEvent(event); |
| } |
| |
| // DeviceEventRouter overrides. |
| bool IsExternalStorageDisabled() override { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| return profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled); |
| } |
| |
| private: |
| Profile* const profile_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DeviceEventRouterImpl); |
| }; |
| |
| class DriveFsEventRouterImpl : public DriveFsEventRouter { |
| public: |
| DriveFsEventRouterImpl(const DriveFsEventRouterImpl&) = delete; |
| DriveFsEventRouterImpl( |
| SystemNotificationManager* notification_manager, |
| Profile* profile, |
| const std::map<base::FilePath, std::unique_ptr<FileWatcher>>* |
| file_watchers) |
| : DriveFsEventRouter(notification_manager), |
| profile_(profile), |
| file_watchers_(file_watchers) {} |
| |
| DriveFsEventRouterImpl& operator=(const DriveFsEventRouterImpl&) = delete; |
| |
| private: |
| std::set<GURL> GetEventListenerURLs(const std::string& event_name) override { |
| const extensions::EventListenerMap::ListenerList& listeners = |
| extensions::EventRouter::Get(profile_) |
| ->listeners() |
| .GetEventListenersByName(event_name); |
| std::set<GURL> urls; |
| for (const auto& listener : listeners) { |
| if (!listener->extension_id().empty()) { |
| urls.insert(extensions::Extension::GetBaseURLFromExtensionId( |
| listener->extension_id())); |
| } else { |
| urls.insert(listener->listener_url()); |
| } |
| } |
| return urls; |
| } |
| |
| GURL ConvertDrivePathToFileSystemUrl(const base::FilePath& file_path, |
| const GURL& listener_url) override { |
| GURL url; |
| file_manager::util::ConvertAbsoluteFilePathToFileSystemUrl( |
| profile_, |
| base::FilePath(DriveIntegrationServiceFactory::FindForProfile(profile_) |
| ->GetMountPointPath() |
| .value() + |
| file_path.value()), |
| listener_url, &url); |
| return url; |
| } |
| |
| std::string GetDriveFileSystemName() override { |
| return DriveIntegrationServiceFactory::FindForProfile(profile_) |
| ->GetMountPointPath() |
| .BaseName() |
| .value(); |
| } |
| |
| bool IsPathWatched(const base::FilePath& path) override { |
| base::FilePath absolute_path = |
| DriveIntegrationServiceFactory::FindForProfile(profile_) |
| ->GetMountPointPath(); |
| return base::FilePath("/").AppendRelativePath(path, &absolute_path) && |
| base::Contains(*file_watchers_, absolute_path); |
| } |
| |
| void BroadcastEvent(extensions::events::HistogramValue histogram_value, |
| const std::string& event_name, |
| std::vector<base::Value> event_args) override { |
| std::unique_ptr<extensions::Event> event = |
| std::make_unique<extensions::Event>(histogram_value, event_name, |
| std::move(event_args)); |
| system_notification_manager()->HandleEvent(*event.get()); |
| extensions::EventRouter::Get(profile_)->BroadcastEvent(std::move(event)); |
| } |
| |
| Profile* const profile_; |
| const std::map<base::FilePath, std::unique_ptr<FileWatcher>>* const |
| file_watchers_; |
| }; |
| |
| } // namespace |
| |
| EventRouter::EventRouter(Profile* profile) |
| : pref_change_registrar_(std::make_unique<PrefChangeRegistrar>()), |
| profile_(profile), |
| notification_manager_( |
| std::make_unique<SystemNotificationManager>(profile)), |
| device_event_router_( |
| std::make_unique<DeviceEventRouterImpl>(notification_manager_.get(), |
| profile)), |
| drivefs_event_router_( |
| std::make_unique<DriveFsEventRouterImpl>(notification_manager_.get(), |
| profile, |
| &file_watchers_)), |
| dispatch_directory_change_event_impl_( |
| base::BindRepeating(&EventRouter::DispatchDirectoryChangeEventImpl, |
| base::Unretained(this))) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| // Notification manager can call into Drive FS for dialog handling. |
| notification_manager_->SetDriveFSEventRouter(drivefs_event_router_.get()); |
| ObserveEvents(); |
| } |
| |
| EventRouter::~EventRouter() = default; |
| |
| void EventRouter::OnIntentFiltersUpdated( |
| const absl::optional<std::string>& package_name) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| BroadcastEvent(profile_, |
| extensions::events::FILE_MANAGER_PRIVATE_ON_APPS_UPDATED, |
| file_manager_private::OnAppsUpdated::kEventName, |
| file_manager_private::OnAppsUpdated::Create()); |
| } |
| |
| void EventRouter::Shutdown() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| ash::TabletMode* tablet_mode = ash::TabletMode::Get(); |
| if (tablet_mode) |
| tablet_mode->RemoveObserver(this); |
| |
| auto* intent_helper = |
| arc::ArcIntentHelperBridge::GetForBrowserContext(profile_); |
| if (intent_helper) |
| intent_helper->RemoveObserver(this); |
| |
| chromeos::system::TimezoneSettings::GetInstance()->RemoveObserver(this); |
| |
| DLOG_IF(WARNING, !file_watchers_.empty()) |
| << "Not all file watchers are " |
| << "removed. This can happen when the Files app is open during shutdown."; |
| file_watchers_.clear(); |
| DCHECK(profile_); |
| |
| pref_change_registrar_->RemoveAll(); |
| |
| content::GetNetworkConnectionTracker()->RemoveNetworkConnectionObserver(this); |
| |
| extensions::ExtensionRegistry::Get(profile_)->RemoveObserver(this); |
| |
| DriveIntegrationService* const integration_service = |
| DriveIntegrationServiceFactory::FindForProfile(profile_); |
| if (integration_service) { |
| integration_service->RemoveObserver(this); |
| integration_service->GetDriveFsHost()->RemoveObserver( |
| drivefs_event_router_.get()); |
| integration_service->GetDriveFsHost()->set_dialog_handler({}); |
| } |
| |
| VolumeManager* const volume_manager = VolumeManager::Get(profile_); |
| if (volume_manager) { |
| volume_manager->RemoveObserver(this); |
| volume_manager->RemoveObserver(device_event_router_.get()); |
| } |
| |
| chromeos::PowerManagerClient* const power_manager_client = |
| chromeos::PowerManagerClient::Get(); |
| power_manager_client->RemoveObserver(device_event_router_.get()); |
| |
| profile_ = nullptr; |
| } |
| |
| void EventRouter::ObserveEvents() { |
| DCHECK(profile_); |
| |
| if (!chromeos::LoginState::IsInitialized() || |
| !chromeos::LoginState::Get()->IsUserLoggedIn()) { |
| return; |
| } |
| |
| // Ignore device events for the first few seconds. |
| device_event_router_->Startup(); |
| |
| // VolumeManager's construction triggers DriveIntegrationService's |
| // construction, so it is necessary to call VolumeManager's Get before |
| // accessing DriveIntegrationService. |
| VolumeManager* const volume_manager = VolumeManager::Get(profile_); |
| if (volume_manager) { |
| volume_manager->AddObserver(this); |
| volume_manager->AddObserver(device_event_router_.get()); |
| } |
| |
| chromeos::PowerManagerClient* const power_manager_client = |
| chromeos::PowerManagerClient::Get(); |
| power_manager_client->AddObserver(device_event_router_.get()); |
| |
| DriveIntegrationService* const integration_service = |
| DriveIntegrationServiceFactory::FindForProfile(profile_); |
| if (integration_service) { |
| integration_service->AddObserver(this); |
| integration_service->GetDriveFsHost()->AddObserver( |
| drivefs_event_router_.get()); |
| integration_service->GetDriveFsHost()->set_dialog_handler( |
| base::BindRepeating(&EventRouter::DisplayDriveConfirmDialog, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| content::GetNetworkConnectionTracker()->AddNetworkConnectionObserver(this); |
| |
| extensions::ExtensionRegistry::Get(profile_)->AddObserver(this); |
| |
| pref_change_registrar_->Init(profile_->GetPrefs()); |
| auto callback = base::BindRepeating(&EventRouter::OnFileManagerPrefsChanged, |
| weak_factory_.GetWeakPtr()); |
| pref_change_registrar_->Add(drive::prefs::kDisableDriveOverCellular, |
| callback); |
| pref_change_registrar_->Add(drive::prefs::kDisableDrive, callback); |
| pref_change_registrar_->Add(prefs::kSearchSuggestEnabled, callback); |
| pref_change_registrar_->Add(prefs::kUse24HourClock, callback); |
| pref_change_registrar_->Add( |
| crostini::prefs::kCrostiniEnabled, |
| base::BindRepeating( |
| &EventRouter::OnCrostiniChanged, weak_factory_.GetWeakPtr(), |
| crostini::kCrostiniDefaultVmName, crostini::prefs::kCrostiniEnabled, |
| file_manager_private::CROSTINI_EVENT_TYPE_ENABLE, |
| file_manager_private::CROSTINI_EVENT_TYPE_DISABLE)); |
| pref_change_registrar_->Add(arc::prefs::kArcEnabled, callback); |
| pref_change_registrar_->Add(arc::prefs::kArcHasAccessToRemovableMedia, |
| callback); |
| |
| auto plugin_vm_callback = base::BindRepeating(&EventRouter::OnPluginVmChanged, |
| weak_factory_.GetWeakPtr()); |
| plugin_vm_subscription_ = |
| std::make_unique<plugin_vm::PluginVmPolicySubscription>( |
| profile_, base::BindRepeating([](base::RepeatingClosure closure, |
| bool is_allowed) { closure.Run(); }, |
| plugin_vm_callback)); |
| pref_change_registrar_->Add(plugin_vm::prefs::kPluginVmImageExists, |
| plugin_vm_callback); |
| |
| chromeos::system::TimezoneSettings::GetInstance()->AddObserver(this); |
| |
| auto* intent_helper = |
| arc::ArcIntentHelperBridge::GetForBrowserContext(profile_); |
| if (intent_helper) |
| intent_helper->AddObserver(this); |
| |
| auto* guest_os_share_path = |
| guest_os::GuestOsSharePath::GetForProfile(profile_); |
| if (guest_os_share_path) |
| guest_os_share_path->AddObserver(this); |
| |
| ash::TabletMode* tablet_mode = ash::TabletMode::Get(); |
| if (tablet_mode) |
| tablet_mode->AddObserver(this); |
| } |
| |
| // File watch setup routines. |
| void EventRouter::AddFileWatch(const base::FilePath& local_path, |
| const base::FilePath& virtual_path, |
| const url::Origin& listener_origin, |
| BoolCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(!callback.is_null()); |
| |
| auto iter = file_watchers_.find(local_path); |
| if (iter == file_watchers_.end()) { |
| std::unique_ptr<FileWatcher> watcher(new FileWatcher(virtual_path)); |
| watcher->AddListener(listener_origin); |
| watcher->WatchLocalFile( |
| profile_, local_path, |
| base::BindRepeating(&EventRouter::HandleFileWatchNotification, |
| weak_factory_.GetWeakPtr()), |
| std::move(callback)); |
| |
| file_watchers_[local_path] = std::move(watcher); |
| } else { |
| iter->second->AddListener(listener_origin); |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), true)); |
| } |
| } |
| |
| void EventRouter::RemoveFileWatch(const base::FilePath& local_path, |
| const url::Origin& listener_origin) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| auto iter = file_watchers_.find(local_path); |
| if (iter == file_watchers_.end()) |
| return; |
| // Remove the watcher if |local_path| is no longer watched by any extensions. |
| iter->second->RemoveListener(listener_origin); |
| if (iter->second->GetListeners().empty()) |
| file_watchers_.erase(iter); |
| } |
| |
| void EventRouter::OnCopyStarted(int copy_id, |
| const GURL& source_url, |
| const GURL& destination_url, |
| int64_t space_needed) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| file_manager_private::CopyOrMoveProgressStatus status; |
| // Send started event. |
| status.type = file_manager_private::COPY_OR_MOVE_PROGRESS_STATUS_TYPE_BEGIN; |
| status.source_url = std::make_unique<std::string>(source_url.spec()); |
| status.destination_url = |
| std::make_unique<std::string>(destination_url.spec()); |
| // Use the bytes copied member to store space needed for this event. |
| status.size = std::make_unique<double>(space_needed); |
| |
| notification_manager_->HandleCopyStart(copy_id, status); |
| } |
| |
| void EventRouter::OnCopyCompleted(int copy_id, |
| const GURL& source_url, |
| const GURL& destination_url, |
| base::File::Error error) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| file_manager_private::CopyOrMoveProgressStatus status; |
| if (error == base::File::FILE_OK) { |
| // Send success event. |
| status.type = |
| file_manager_private::COPY_OR_MOVE_PROGRESS_STATUS_TYPE_SUCCESS; |
| status.source_url = std::make_unique<std::string>(source_url.spec()); |
| status.destination_url = |
| std::make_unique<std::string>(destination_url.spec()); |
| } else { |
| // Send error event. |
| status.type = file_manager_private::COPY_OR_MOVE_PROGRESS_STATUS_TYPE_ERROR; |
| status.error = std::make_unique<std::string>(FileErrorToErrorName(error)); |
| } |
| |
| notification_manager_->HandleCopyEvent(copy_id, status); |
| BroadcastEvent(profile_, |
| extensions::events::FILE_MANAGER_PRIVATE_ON_COPY_PROGRESS, |
| file_manager_private::OnCopyProgress::kEventName, |
| file_manager_private::OnCopyProgress::Create(copy_id, status)); |
| } |
| |
| void EventRouter::OnCopyProgress( |
| int copy_id, |
| storage::FileSystemOperation::CopyOrMoveProgressType type, |
| const GURL& source_url, |
| const GURL& destination_url, |
| int64_t size) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| file_manager_private::CopyOrMoveProgressStatus status; |
| status.type = CopyOrMoveProgressTypeToCopyOrMoveProgressStatusType(type); |
| status.source_url = std::make_unique<std::string>(source_url.spec()); |
| if (type == storage::FileSystemOperation::CopyOrMoveProgressType::kError) { |
| // For cross-filesystems moves, no destination_url is provided when an error |
| // occurs. This translates into to a non-valid destination GURL. |
| // status.destination_url should never be used in this case. |
| status.destination_url = |
| std::make_unique<std::string>(destination_url.possibly_invalid_spec()); |
| } else if (type != storage::FileSystemOperation::CopyOrMoveProgressType:: |
| kEndRemoveSource) { |
| status.destination_url = |
| std::make_unique<std::string>(destination_url.spec()); |
| } |
| |
| if (type == storage::FileSystemOperation::CopyOrMoveProgressType::kError) |
| status.error = std::make_unique<std::string>( |
| FileErrorToErrorName(base::File::FILE_ERROR_FAILED)); |
| if (type == storage::FileSystemOperation::CopyOrMoveProgressType::kProgress) |
| status.size = std::make_unique<double>(size); |
| |
| // Discard error progress since current JS code cannot handle this properly. |
| // TODO(yawano): Remove this after JS side is implemented correctly. |
| if (type == storage::FileSystemOperation::CopyOrMoveProgressType::kError) |
| return; |
| |
| // Should not skip events other than TYPE_PROGRESS. |
| const bool always = |
| status.type != |
| file_manager_private::COPY_OR_MOVE_PROGRESS_STATUS_TYPE_PROGRESS; |
| if (!ShouldSendProgressEvent(always, &last_copy_progress_event_)) |
| return; |
| |
| notification_manager_->HandleCopyEvent(copy_id, status); |
| BroadcastEvent(profile_, |
| extensions::events::FILE_MANAGER_PRIVATE_ON_COPY_PROGRESS, |
| file_manager_private::OnCopyProgress::kEventName, |
| file_manager_private::OnCopyProgress::Create(copy_id, status)); |
| } |
| |
| void EventRouter::OnWatcherManagerNotification( |
| const storage::FileSystemURL& file_system_url, |
| const url::Origin& listener_origin, |
| storage::WatcherManager::ChangeType /* change_type */) { |
| std::vector<url::Origin> listeners = {listener_origin}; |
| |
| DispatchDirectoryChangeEvent(file_system_url.virtual_path(), |
| false /* error */, listeners); |
| } |
| |
| void EventRouter::OnConnectionChanged(network::mojom::ConnectionType type) { |
| NotifyDriveConnectionStatusChanged(); |
| } |
| |
| void EventRouter::OnExtensionLoaded(content::BrowserContext* browser_context, |
| const extensions::Extension* extension) { |
| NotifyDriveConnectionStatusChanged(); |
| } |
| |
| void EventRouter::OnExtensionUnloaded( |
| content::BrowserContext* browser_context, |
| const extensions::Extension* extension, |
| extensions::UnloadedExtensionReason reason) { |
| NotifyDriveConnectionStatusChanged(); |
| } |
| |
| void EventRouter::TimezoneChanged(const icu::TimeZone& timezone) { |
| OnFileManagerPrefsChanged(); |
| } |
| |
| void EventRouter::OnFileManagerPrefsChanged() { |
| DCHECK(profile_); |
| DCHECK(extensions::EventRouter::Get(profile_)); |
| |
| BroadcastEvent( |
| profile_, extensions::events::FILE_MANAGER_PRIVATE_ON_PREFERENCES_CHANGED, |
| file_manager_private::OnPreferencesChanged::kEventName, |
| file_manager_private::OnPreferencesChanged::Create()); |
| } |
| |
| void EventRouter::HandleFileWatchNotification(const base::FilePath& local_path, |
| bool got_error) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| auto iter = file_watchers_.find(local_path); |
| if (iter == file_watchers_.end()) { |
| return; |
| } |
| |
| DispatchDirectoryChangeEvent(iter->second->virtual_path(), got_error, |
| iter->second->GetListeners()); |
| } |
| |
| void EventRouter::DispatchDirectoryChangeEvent( |
| const base::FilePath& virtual_path, |
| bool got_error, |
| const std::vector<url::Origin>& listeners) { |
| dispatch_directory_change_event_impl_.Run(virtual_path, got_error, listeners); |
| } |
| |
| void EventRouter::DispatchDirectoryChangeEventImpl( |
| const base::FilePath& virtual_path, |
| bool got_error, |
| const std::vector<url::Origin>& listeners) { |
| DCHECK(profile_); |
| |
| for (const url::Origin& origin : listeners) { |
| FileDefinition file_definition; |
| file_definition.virtual_path = virtual_path; |
| // TODO(mtomasz): Add support for watching files in File System Provider |
| // API. |
| file_definition.is_directory = true; |
| |
| file_manager::util::ConvertFileDefinitionToEntryDefinition( |
| util::GetFileSystemContextForSourceURL(profile_, origin.GetURL()), |
| origin, file_definition, |
| base::BindOnce( |
| &EventRouter::DispatchDirectoryChangeEventWithEntryDefinition, |
| weak_factory_.GetWeakPtr(), got_error)); |
| } |
| } |
| |
| void EventRouter::DispatchDirectoryChangeEventWithEntryDefinition( |
| bool watcher_error, |
| const EntryDefinition& entry_definition) { |
| // TODO(mtomasz): Add support for watching files in File System Provider API. |
| if (entry_definition.error != base::File::FILE_OK || |
| !entry_definition.is_directory) { |
| DVLOG(1) << "Unable to dispatch event because resolving the directory " |
| << "entry definition failed."; |
| return; |
| } |
| |
| file_manager_private::FileWatchEvent event; |
| event.event_type = watcher_error |
| ? file_manager_private::FILE_WATCH_EVENT_TYPE_ERROR |
| : file_manager_private::FILE_WATCH_EVENT_TYPE_CHANGED; |
| |
| event.entry.additional_properties.SetString( |
| "fileSystemName", entry_definition.file_system_name); |
| event.entry.additional_properties.SetString( |
| "fileSystemRoot", entry_definition.file_system_root_url); |
| event.entry.additional_properties.SetString( |
| "fileFullPath", "/" + entry_definition.full_path.value()); |
| event.entry.additional_properties.SetBoolean("fileIsDirectory", |
| entry_definition.is_directory); |
| |
| BroadcastEvent(profile_, |
| extensions::events::FILE_MANAGER_PRIVATE_ON_DIRECTORY_CHANGED, |
| file_manager_private::OnDirectoryChanged::kEventName, |
| file_manager_private::OnDirectoryChanged::Create(event)); |
| } |
| |
| void EventRouter::OnDiskAdded(const Disk& disk, bool mounting) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| // Do nothing. |
| } |
| |
| void EventRouter::OnDiskRemoved(const Disk& disk) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| // Do nothing. |
| } |
| |
| void EventRouter::OnDeviceAdded(const std::string& device_path) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| // Do nothing. |
| } |
| |
| void EventRouter::OnDeviceRemoved(const std::string& device_path) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| // Do nothing. |
| } |
| |
| void EventRouter::OnVolumeMounted(chromeos::MountError error_code, |
| const Volume& volume) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| // profile_ is NULL if ShutdownOnUIThread() is called earlier. This can |
| // happen at shutdown. This should be removed after removing Drive mounting |
| // code in addMount. (addMount -> OnFileSystemMounted -> OnVolumeMounted is |
| // the only path to come here after Shutdown is called). |
| if (!profile_) |
| return; |
| |
| DispatchMountCompletedEvent( |
| file_manager_private::MOUNT_COMPLETED_EVENT_TYPE_MOUNT, error_code, |
| volume); |
| |
| // TODO(mtomasz): Move VolumeManager and part of the event router outside of |
| // file_manager, so there is no dependency between File System API and the |
| // file_manager code. |
| extensions::file_system_api::DispatchVolumeListChangeEvent(profile_); |
| } |
| |
| void EventRouter::OnVolumeUnmounted(chromeos::MountError error_code, |
| const Volume& volume) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DispatchMountCompletedEvent( |
| file_manager_private::MOUNT_COMPLETED_EVENT_TYPE_UNMOUNT, error_code, |
| volume); |
| } |
| |
| void EventRouter::DispatchMountCompletedEvent( |
| file_manager_private::MountCompletedEventType event_type, |
| chromeos::MountError error, |
| const Volume& volume) { |
| // Build an event object. |
| file_manager_private::MountCompletedEvent event; |
| event.event_type = event_type; |
| event.status = MountErrorToMountCompletedStatus(error); |
| util::VolumeToVolumeMetadata(profile_, volume, &event.volume_metadata); |
| event.should_notify = |
| ShouldShowNotificationForVolume(profile_, *device_event_router_, volume); |
| notification_manager_->HandleMountCompletedEvent(event, volume); |
| BroadcastEvent(profile_, |
| extensions::events::FILE_MANAGER_PRIVATE_ON_MOUNT_COMPLETED, |
| file_manager_private::OnMountCompleted::kEventName, |
| file_manager_private::OnMountCompleted::Create(event)); |
| } |
| |
| void EventRouter::OnFormatStarted(const std::string& device_path, |
| const std::string& device_label, |
| bool success) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| // Do nothing. |
| } |
| |
| void EventRouter::OnFormatCompleted(const std::string& device_path, |
| const std::string& device_label, |
| bool success) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| // Do nothing. |
| } |
| |
| void EventRouter::OnPartitionStarted(const std::string& device_path, |
| const std::string& device_label, |
| bool success) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| // Do nothing. |
| } |
| |
| void EventRouter::OnPartitionCompleted(const std::string& device_path, |
| const std::string& device_label, |
| bool success) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| // Do nothing. |
| } |
| |
| void EventRouter::OnRenameStarted(const std::string& device_path, |
| const std::string& device_label, |
| bool success) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| // Do nothing. |
| } |
| |
| void EventRouter::OnRenameCompleted(const std::string& device_path, |
| const std::string& device_label, |
| bool success) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| // Do nothing. |
| } |
| |
| void EventRouter::SetDispatchDirectoryChangeEventImplForTesting( |
| const DispatchDirectoryChangeEventImplCallback& callback) { |
| dispatch_directory_change_event_impl_ = callback; |
| } |
| |
| void EventRouter::OnFileSystemMountFailed() { |
| OnFileManagerPrefsChanged(); |
| } |
| |
| // Send crostini share, unshare event. |
| void EventRouter::SendCrostiniEvent( |
| file_manager_private::CrostiniEventType event_type, |
| const std::string& vm_name, |
| const base::FilePath& path) { |
| std::string mount_name; |
| std::string file_system_name; |
| std::string full_path; |
| if (!util::ExtractMountNameFileSystemNameFullPath( |
| path, &mount_name, &file_system_name, &full_path)) |
| return; |
| |
| const std::string event_name( |
| file_manager_private::OnCrostiniChanged::kEventName); |
| const extensions::EventListenerMap::ListenerList& listeners = |
| extensions::EventRouter::Get(profile_) |
| ->listeners() |
| .GetEventListenersByName(event_name); |
| |
| // We handle two types of listeners, those with extension IDs and those with |
| // listener URL. For listeners with extension IDs we use direct dispatch. For |
| // listeners with listener URL we use a broadcast. |
| std::set<std::string> extension_ids; |
| std::set<url::Origin> origins; |
| for (auto const& listener : listeners) { |
| if (!listener->extension_id().empty()) { |
| extension_ids.insert(listener->extension_id()); |
| } else if (listener->listener_url().is_valid()) { |
| origins.insert(url::Origin::Create(listener->listener_url())); |
| } |
| } |
| |
| for (const std::string& extension_id : extension_ids) { |
| url::Origin origin = url::Origin::Create( |
| extensions::Extension::GetBaseURLFromExtensionId(extension_id)); |
| file_manager_private::CrostiniEvent event; |
| PopulateCrostiniEvent(event, event_type, vm_name, origin, mount_name, |
| file_system_name, full_path); |
| DispatchEventToExtension( |
| profile_, extension_id, |
| extensions::events::FILE_MANAGER_PRIVATE_ON_CROSTINI_CHANGED, |
| event_name, file_manager_private::OnCrostiniChanged::Create(event)); |
| } |
| for (const url::Origin& origin : origins) { |
| file_manager_private::CrostiniEvent event; |
| PopulateCrostiniEvent(event, event_type, vm_name, origin, mount_name, |
| file_system_name, full_path); |
| BroadcastEvent( |
| profile_, extensions::events::FILE_MANAGER_PRIVATE_ON_CROSTINI_CHANGED, |
| event_name, file_manager_private::OnCrostiniChanged::Create(event)); |
| } |
| } |
| |
| // static |
| void EventRouter::PopulateCrostiniEvent( |
| file_manager_private::CrostiniEvent& event, |
| file_manager_private::CrostiniEventType event_type, |
| const std::string& vm_name, |
| const url::Origin& origin, |
| const std::string& mount_name, |
| const std::string& file_system_name, |
| const std::string& full_path) { |
| event.event_type = event_type; |
| event.vm_name = vm_name; |
| file_manager_private::CrostiniEvent::EntriesType entry; |
| entry.additional_properties.SetString( |
| "fileSystemRoot", |
| storage::GetExternalFileSystemRootURIString(origin.GetURL(), mount_name)); |
| entry.additional_properties.SetString("fileSystemName", file_system_name); |
| entry.additional_properties.SetString("fileFullPath", full_path); |
| entry.additional_properties.SetBoolean("fileIsDirectory", true); |
| event.entries.emplace_back(std::move(entry)); |
| } |
| |
| void EventRouter::OnShare(const std::string& vm_name, |
| const base::FilePath& path, |
| bool persist) { |
| if (persist) { |
| SendCrostiniEvent(file_manager_private::CROSTINI_EVENT_TYPE_SHARE, vm_name, |
| path); |
| } |
| } |
| |
| void EventRouter::OnUnshare(const std::string& vm_name, |
| const base::FilePath& path) { |
| SendCrostiniEvent(file_manager_private::CROSTINI_EVENT_TYPE_UNSHARE, vm_name, |
| path); |
| } |
| |
| void EventRouter::OnTabletModeStarted() { |
| BroadcastEvent( |
| profile_, extensions::events::FILE_MANAGER_PRIVATE_ON_TABLET_MODE_CHANGED, |
| file_manager_private::OnTabletModeChanged::kEventName, |
| file_manager_private::OnTabletModeChanged::Create(/*enabled=*/true)); |
| } |
| |
| void EventRouter::OnTabletModeEnded() { |
| BroadcastEvent( |
| profile_, extensions::events::FILE_MANAGER_PRIVATE_ON_TABLET_MODE_CHANGED, |
| file_manager_private::OnTabletModeChanged::kEventName, |
| file_manager_private::OnTabletModeChanged::Create(/*enabled=*/false)); |
| } |
| |
| void EventRouter::OnCrostiniChanged( |
| const std::string& vm_name, |
| const std::string& pref_name, |
| extensions::api::file_manager_private::CrostiniEventType pref_true, |
| extensions::api::file_manager_private::CrostiniEventType pref_false) { |
| file_manager_private::CrostiniEvent event; |
| event.vm_name = vm_name; |
| event.event_type = |
| profile_->GetPrefs()->GetBoolean(pref_name) ? pref_true : pref_false; |
| BroadcastEvent(profile_, |
| extensions::events::FILE_MANAGER_PRIVATE_ON_CROSTINI_CHANGED, |
| file_manager_private::OnCrostiniChanged::kEventName, |
| file_manager_private::OnCrostiniChanged::Create(event)); |
| } |
| |
| void EventRouter::OnPluginVmChanged() { |
| file_manager_private::CrostiniEvent event; |
| event.vm_name = plugin_vm::kPluginVmName; |
| event.event_type = plugin_vm::PluginVmFeatures::Get()->IsEnabled(profile_) |
| ? file_manager_private::CROSTINI_EVENT_TYPE_ENABLE |
| : file_manager_private::CROSTINI_EVENT_TYPE_DISABLE; |
| BroadcastEvent(profile_, |
| extensions::events::FILE_MANAGER_PRIVATE_ON_CROSTINI_CHANGED, |
| file_manager_private::OnCrostiniChanged::kEventName, |
| file_manager_private::OnCrostiniChanged::Create(event)); |
| } |
| |
| void EventRouter::NotifyDriveConnectionStatusChanged() { |
| DCHECK(profile_); |
| DCHECK(extensions::EventRouter::Get(profile_)); |
| |
| BroadcastEvent( |
| profile_, |
| extensions::events:: |
| FILE_MANAGER_PRIVATE_ON_DRIVE_CONNECTION_STATUS_CHANGED, |
| file_manager_private::OnDriveConnectionStatusChanged::kEventName, |
| file_manager_private::OnDriveConnectionStatusChanged::Create()); |
| } |
| |
| void EventRouter::DropFailedPluginVmDirectoryNotShared() { |
| file_manager_private::CrostiniEvent event; |
| event.vm_name = plugin_vm::kPluginVmName; |
| event.event_type = file_manager_private:: |
| CROSTINI_EVENT_TYPE_DROP_FAILED_PLUGIN_VM_DIRECTORY_NOT_SHARED; |
| BroadcastEvent(profile_, |
| extensions::events::FILE_MANAGER_PRIVATE_ON_CROSTINI_CHANGED, |
| file_manager_private::OnCrostiniChanged::kEventName, |
| file_manager_private::OnCrostiniChanged::Create(event)); |
| } |
| |
| void EventRouter::DisplayDriveConfirmDialog( |
| const drivefs::mojom::DialogReason& reason, |
| base::OnceCallback<void(drivefs::mojom::DialogResult)> callback) { |
| drivefs_event_router_->DisplayConfirmDialog(reason, std::move(callback)); |
| } |
| |
| void EventRouter::OnDriveDialogResult(drivefs::mojom::DialogResult result) { |
| drivefs_event_router_->OnDialogResult(result); |
| } |
| |
| base::WeakPtr<EventRouter> EventRouter::GetWeakPtr() { |
| return weak_factory_.GetWeakPtr(); |
| } |
| |
| } // namespace file_manager |