blob: 95341419619089602428965d4ef04c1c6abd0b63 [file] [log] [blame]
// Copyright 2018 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/drivefs_event_router.h"
#include "base/files/file_path.h"
#include "base/strings/strcat.h"
#include "base/values.h"
#include "chrome/common/extensions/api/file_manager_private.h"
#include "extensions/common/extension.h"
namespace file_manager {
namespace file_manager_private = extensions::api::file_manager_private;
namespace {
file_manager_private::TransferState ConvertItemEventState(
drivefs::mojom::ItemEvent::State state) {
switch (state) {
case drivefs::mojom::ItemEvent::State::kQueued:
case drivefs::mojom::ItemEvent::State::kInProgress:
return file_manager_private::TRANSFER_STATE_IN_PROGRESS;
case drivefs::mojom::ItemEvent::State::kCompleted:
return file_manager_private::TRANSFER_STATE_COMPLETED;
case drivefs::mojom::ItemEvent::State::kFailed:
return file_manager_private::TRANSFER_STATE_FAILED;
}
}
bool IsItemEventCompleted(drivefs::mojom::ItemEvent::State state) {
switch (state) {
case drivefs::mojom::ItemEvent::State::kQueued:
case drivefs::mojom::ItemEvent::State::kInProgress:
return false;
case drivefs::mojom::ItemEvent::State::kCompleted:
case drivefs::mojom::ItemEvent::State::kFailed:
return true;
}
return false;
}
file_manager_private::DriveConfirmDialogType ConvertDialogReasonType(
drivefs::mojom::DialogReason::Type type) {
switch (type) {
case drivefs::mojom::DialogReason::Type::kEnableDocsOffline:
return file_manager_private::
DRIVE_CONFIRM_DIALOG_TYPE_ENABLE_DOCS_OFFLINE;
}
}
} // namespace
DriveFsEventRouter::DriveFsEventRouter(
SystemNotificationManager* notification_manager)
: notification_manager_(notification_manager) {}
DriveFsEventRouter::~DriveFsEventRouter() = default;
DriveFsEventRouter::SyncingStatusState::SyncingStatusState() = default;
DriveFsEventRouter::SyncingStatusState::~SyncingStatusState() = default;
void DriveFsEventRouter::OnUnmounted() {
sync_status_state_.completed_bytes = 0;
sync_status_state_.group_id_to_bytes_to_transfer.clear();
pin_status_state_.completed_bytes = 0;
pin_status_state_.group_id_to_bytes_to_transfer.clear();
// Ensure any existing sync progress indicator is cleared.
file_manager_private::FileTransferStatus sync_status;
sync_status.transfer_state = file_manager_private::TRANSFER_STATE_FAILED;
sync_status.hide_when_zero_jobs = true;
file_manager_private::FileTransferStatus pin_status;
pin_status.transfer_state = file_manager_private::TRANSFER_STATE_FAILED;
pin_status.hide_when_zero_jobs = true;
BroadcastOnFileTransfersUpdatedEvent(sync_status);
BroadcastOnPinTransfersUpdatedEvent(pin_status);
dialog_callback_.Reset();
}
file_manager_private::FileTransferStatus
DriveFsEventRouter::CreateFileTransferStatus(
const std::vector<drivefs::mojom::ItemEvent*>& item_events,
SyncingStatusState* state) {
int64_t total_bytes_transferred = 0;
int64_t total_bytes_to_transfer = 0;
int num_files_syncing = 0;
bool any_in_progress = false;
for (const auto* item : item_events) {
if (IsItemEventCompleted(item->state)) {
auto it = state->group_id_to_bytes_to_transfer.find(item->group_id);
if (it != state->group_id_to_bytes_to_transfer.end()) {
state->completed_bytes += it->second;
state->group_id_to_bytes_to_transfer.erase(it);
}
} else {
total_bytes_transferred += item->bytes_transferred;
total_bytes_to_transfer += item->bytes_to_transfer;
++num_files_syncing;
if (item->state == drivefs::mojom::ItemEvent::State::kInProgress) {
any_in_progress = true;
}
if (item->bytes_to_transfer) {
state->group_id_to_bytes_to_transfer[item->group_id] =
item->bytes_to_transfer;
}
}
}
auto completed_bytes = state->completed_bytes;
if (num_files_syncing == 0) {
state->completed_bytes = 0;
state->group_id_to_bytes_to_transfer.clear();
}
file_manager_private::FileTransferStatus status;
status.hide_when_zero_jobs = true;
if ((completed_bytes == 0 && !any_in_progress) || item_events.empty()) {
status.transfer_state = file_manager_private::TRANSFER_STATE_COMPLETED;
return status;
}
total_bytes_transferred += completed_bytes;
total_bytes_to_transfer += completed_bytes;
status.num_total_jobs = num_files_syncing;
status.processed = total_bytes_transferred;
status.total = total_bytes_to_transfer;
return status;
}
void DriveFsEventRouter::OnSyncingStatusUpdate(
const drivefs::mojom::SyncingStatus& syncing_status) {
std::vector<drivefs::mojom::ItemEvent*> sync_events, pin_events;
for (const auto& item : syncing_status.item_events) {
if (item->reason == drivefs::mojom::ItemEventReason::kPin) {
pin_events.push_back(item.get());
} else {
sync_events.push_back(item.get());
}
}
auto sync_status = CreateFileTransferStatus(sync_events, &sync_status_state_);
auto pin_status = CreateFileTransferStatus(pin_events, &pin_status_state_);
auto urls = GetEventListenerURLs(
file_manager_private::OnFileTransfersUpdated::kEventName);
if (sync_status.total == 0) {
BroadcastOnFileTransfersUpdatedEvent(sync_status);
} else {
for (const auto* item : sync_events) {
sync_status.transfer_state = ConvertItemEventState(item->state);
base::FilePath path(item->path);
for (const auto& listener_url : urls) {
sync_status.file_url =
ConvertDrivePathToFileSystemUrl(path, listener_url).spec();
BroadcastOnFileTransfersUpdatedEvent(sync_status);
}
}
}
if (pin_status.total == 0) {
BroadcastOnPinTransfersUpdatedEvent(pin_status);
} else {
for (const auto* item : pin_events) {
pin_status.transfer_state = ConvertItemEventState(item->state);
base::FilePath path(item->path);
for (const auto& listener_url : urls) {
pin_status.file_url =
ConvertDrivePathToFileSystemUrl(path, listener_url).spec();
BroadcastOnPinTransfersUpdatedEvent(pin_status);
}
}
}
}
void DriveFsEventRouter::OnFilesChanged(
const std::vector<drivefs::mojom::FileChange>& changes) {
// Maps from parent directory to event for that directory.
std::map<base::FilePath,
extensions::api::file_manager_private::FileWatchEvent>
events;
for (const auto& listener_url : GetEventListenerURLs(
file_manager_private::OnDirectoryChanged::kEventName)) {
for (const auto& change : changes) {
auto& event = events[change.path.DirName()];
if (!event.changed_files) {
event.event_type = extensions::api::file_manager_private::
FILE_WATCH_EVENT_TYPE_CHANGED;
event.changed_files = std::make_unique<
std::vector<extensions::api::file_manager_private::FileChange>>();
event.entry.additional_properties.SetStringKey(
"fileSystemRoot", base::StrCat({ConvertDrivePathToFileSystemUrl(
base::FilePath(), listener_url)
.spec(),
"/"}));
event.entry.additional_properties.SetStringKey(
"fileSystemName", GetDriveFileSystemName());
event.entry.additional_properties.SetStringKey(
"fileFullPath", change.path.DirName().value());
event.entry.additional_properties.SetBoolKey("fileIsDirectory", true);
}
event.changed_files->emplace_back();
auto& file_manager_change = event.changed_files->back();
file_manager_change.url =
ConvertDrivePathToFileSystemUrl(change.path, listener_url).spec();
file_manager_change.changes.push_back(
change.type == drivefs::mojom::FileChange::Type::kDelete
? extensions::api::file_manager_private::CHANGE_TYPE_DELETE
: extensions::api::file_manager_private::
CHANGE_TYPE_ADD_OR_UPDATE);
}
for (auto& event : events) {
BroadcastOnDirectoryChangedEvent(event.first, event.second);
}
}
}
void DriveFsEventRouter::OnError(const drivefs::mojom::DriveError& error) {
file_manager_private::DriveSyncErrorEvent event;
switch (error.type) {
case drivefs::mojom::DriveError::Type::kCantUploadStorageFull:
event.type = file_manager_private::DRIVE_SYNC_ERROR_TYPE_NO_SERVER_SPACE;
break;
case drivefs::mojom::DriveError::Type::kCantUploadStorageFullOrganization:
event.type = file_manager_private::
DRIVE_SYNC_ERROR_TYPE_NO_SERVER_SPACE_ORGANIZATION;
break;
case drivefs::mojom::DriveError::Type::kPinningFailedDiskFull:
event.type = file_manager_private::DRIVE_SYNC_ERROR_TYPE_NO_LOCAL_SPACE;
break;
}
for (const auto& listener_url : GetEventListenerURLs(
file_manager_private::OnDriveSyncError::kEventName)) {
event.file_url =
ConvertDrivePathToFileSystemUrl(error.path, listener_url).spec();
BroadcastEvent(extensions::events::FILE_MANAGER_PRIVATE_ON_DRIVE_SYNC_ERROR,
file_manager_private::OnDriveSyncError::kEventName,
file_manager_private::OnDriveSyncError::Create(event));
}
}
void DriveFsEventRouter::DisplayConfirmDialog(
const drivefs::mojom::DialogReason& reason,
base::OnceCallback<void(drivefs::mojom::DialogResult)> callback) {
if (dialog_callback_) {
std::move(callback).Run(drivefs::mojom::DialogResult::kNotDisplayed);
return;
}
auto urls = GetEventListenerURLs(
file_manager_private::OnDriveConfirmDialog::kEventName);
if (urls.empty()) {
std::move(callback).Run(drivefs::mojom::DialogResult::kNotDisplayed);
return;
}
dialog_callback_ = std::move(callback);
file_manager_private::DriveConfirmDialogEvent event;
event.type = ConvertDialogReasonType(reason.type);
for (const auto& listener_url : urls) {
event.file_url =
ConvertDrivePathToFileSystemUrl(reason.path, listener_url).spec();
BroadcastEvent(
extensions::events::FILE_MANAGER_PRIVATE_ON_DRIVE_CONFIRM_DIALOG,
file_manager_private::OnDriveConfirmDialog::kEventName,
file_manager_private::OnDriveConfirmDialog::Create(event));
}
}
void DriveFsEventRouter::OnDialogResult(drivefs::mojom::DialogResult result) {
if (dialog_callback_) {
std::move(dialog_callback_).Run(result);
}
}
void DriveFsEventRouter::BroadcastOnFileTransfersUpdatedEvent(
const extensions::api::file_manager_private::FileTransferStatus& status) {
BroadcastEvent(
extensions::events::FILE_MANAGER_PRIVATE_ON_FILE_TRANSFERS_UPDATED,
file_manager_private::OnFileTransfersUpdated::kEventName,
file_manager_private::OnFileTransfersUpdated::Create(status));
}
void DriveFsEventRouter::BroadcastOnPinTransfersUpdatedEvent(
const extensions::api::file_manager_private::FileTransferStatus& status) {
BroadcastEvent(
extensions::events::FILE_MANAGER_PRIVATE_ON_PIN_TRANSFERS_UPDATED,
file_manager_private::OnPinTransfersUpdated::kEventName,
file_manager_private::OnPinTransfersUpdated::Create(status));
}
void DriveFsEventRouter::BroadcastOnDirectoryChangedEvent(
const base::FilePath& directory,
const extensions::api::file_manager_private::FileWatchEvent& event) {
if (!IsPathWatched(directory)) {
return;
}
BroadcastEvent(extensions::events::FILE_MANAGER_PRIVATE_ON_DIRECTORY_CHANGED,
file_manager_private::OnDirectoryChanged::kEventName,
file_manager_private::OnDirectoryChanged::Create(event));
}
} // namespace file_manager