chromium/ui/file_manager/file_manager/common/js/api.ts

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview Helpers for APIs used within Files app.
 */

import type {FilesAppDirEntry, FilesAppEntry} from '../../common/js/files_app_entry_types.js';
import type {FileKey} from '../../state/state.js';

import {unwrapEntry, urlToEntry} from './entry_utils.js';
import {promisify} from './util.js';

/**
 * Opens a new window for Files SWA.
 */
export async function openWindow(
    params: chrome.fileManagerPrivate.OpenWindowParams|undefined) {
  return promisify<boolean>(chrome.fileManagerPrivate.openWindow, params);
}

export async function resolveIsolatedEntries(isolatedEntries: Entry[]) {
  return promisify<Entry[]>(
      chrome.fileManagerPrivate.resolveIsolatedEntries,
      isolatedEntries.map(e => unwrapEntry(e)));
}

export async function getPreferences() {
  return promisify<chrome.fileManagerPrivate.Preferences>(
      chrome.fileManagerPrivate.getPreferences);
}

export async function validatePathNameLength(
    parentEntry: FilesAppDirEntry|DirectoryEntry, name: string) {
  return promisify<boolean>(
      chrome.fileManagerPrivate.validatePathNameLength,
      unwrapEntry(parentEntry), name);
}

/**
 * Wrap the chrome.fileManagerPrivate.getSizeStats function in an async/await
 * compatible style.
 */
export async function getSizeStats(volumeId: string) {
  return promisify<chrome.fileManagerPrivate.MountPointSizeStats|undefined>(
      chrome.fileManagerPrivate.getSizeStats, volumeId);
}

/**
 * Wrap the chrome.fileManagerPrivate.getDriveQuotaMetadata function in an
 * async/await compatible style.
 */
export async function getDriveQuotaMetadata(entry: Entry|FilesAppEntry) {
  return promisify<chrome.fileManagerPrivate.DriveQuotaMetadata|undefined>(
      chrome.fileManagerPrivate.getDriveQuotaMetadata, unwrapEntry(entry));
}

/**
 * Retrieves the current holding space state, for example the list of items the
 * holding space currently contains.
 */
export async function getHoldingSpaceState() {
  return promisify<chrome.fileManagerPrivate.HoldingSpaceState|undefined>(
      chrome.fileManagerPrivate.getHoldingSpaceState);
}

/**
 * Wrap the chrome.fileManagerPrivate.getDisallowedTransfers function in an
 * async/await compatible style.
 */
export async function getDisallowedTransfers(
    entries: Entry[], destinationEntry: DirectoryEntry|FilesAppDirEntry,
    isMove: boolean) {
  return promisify<Entry[]>(
      chrome.fileManagerPrivate.getDisallowedTransfers,
      entries.map(e => unwrapEntry(e)), unwrapEntry(destinationEntry), isMove);
}

/**
 * Wrap the chrome.fileManagerPrivate.getDlpMetadata function in an async/await
 * compatible style.
 */
export async function getDlpMetadata(entries: Array<Entry|FilesAppEntry>) {
  return promisify<chrome.fileManagerPrivate.DlpMetadata[]>(
      chrome.fileManagerPrivate.getDlpMetadata,
      entries.map(e => unwrapEntry(e)));
}

/**
 * Retrieves the list of components to which the transfer of an Entry is blocked
 * by Data Leak Prevention (DLP) policy.
 */
export async function getDlpBlockedComponents(sourceUrl: string) {
  return promisify<chrome.fileManagerPrivate.VolumeType[]>(
      chrome.fileManagerPrivate.getDlpBlockedComponents, sourceUrl);
}

/**
 * Retrieves Data Leak Prevention (DLP) restriction details.
 */
export async function getDlpRestrictionDetails(sourceUrl: string) {
  return promisify<chrome.fileManagerPrivate.DlpRestrictionDetails[]>(
      chrome.fileManagerPrivate.getDlpRestrictionDetails, sourceUrl);
}

/**
 * Retrieves the caller that created the dialog (Save As/File Picker).
 */
export async function getDialogCaller() {
  return promisify<chrome.fileManagerPrivate.DialogCallerInformation>(
      chrome.fileManagerPrivate.getDialogCaller);
}

/**
 * Lists Guest OSs which support having their files mounted.
 */
export async function listMountableGuests() {
  return promisify<chrome.fileManagerPrivate.MountableGuest[]>(
      chrome.fileManagerPrivate.listMountableGuests);
}

/**
 * Lists Guest OSs which support having their files mounted.
 */
export async function mountGuest(id: number) {
  return promisify<void>(chrome.fileManagerPrivate.mountGuest, id);
}

/*
 * FileSystemEntry helpers
 */

export async function getParentEntry(entry: Entry|
                                     FilesAppEntry): Promise<DirectoryEntry> {
  return new Promise((resolve, reject) => {
    entry.getParent(resolve, reject);
  });
}

export async function moveEntryTo(
    entry: Entry|FilesAppEntry, parent: DirectoryEntry,
    newName: string): Promise<Entry|FilesAppEntry> {
  return new Promise((resolve, reject) => {
    entry.moveTo(parent, newName, resolve, reject);
  });
}

export async function getFile(
    directory: FilesAppDirEntry|DirectoryEntry, filename: string,
    options: Flags|undefined): Promise<FileEntry> {
  return new Promise((resolve, reject) => {
    directory.getFile(filename, options, resolve, reject);
  });
}

export async function getDirectory(
    directory: DirectoryEntry, filename: string,
    options: Flags|undefined): Promise<DirectoryEntry> {
  return new Promise((resolve, reject) => {
    directory.getDirectory(filename, options, resolve, reject);
  });
}

export async function getEntry(
    directory: DirectoryEntry, filename: string, isFile: boolean,
    options?: Flags): Promise<Entry> {
  const getEntry = isFile ? getFile : getDirectory;
  return getEntry(directory, filename, options);
}

/**
 * Starts an IOTask of `type` and returns a taskId that can be used to cancel
 * or identify the ongoing IO operation.
 */
export async function startIOTask(
    type: chrome.fileManagerPrivate.IoTaskType,
    entries: Array<Entry|FilesAppEntry>,
    params: chrome.fileManagerPrivate.IoTaskParams) {
  if (params.destinationFolder) {
    params.destinationFolder =
        unwrapEntry(params.destinationFolder) as DirectoryEntry;
  }
  return promisify<number>(
      chrome.fileManagerPrivate.startIOTask, type,
      entries.map(e => unwrapEntry(e)), params);
}

/**
 * Parses .trashinfo files to retrieve the restore path and deletion date.
 */
export async function parseTrashInfoFiles(entries: Entry[]) {
  return promisify<chrome.fileManagerPrivate.ParsedTrashInfoFile[]>(
      chrome.fileManagerPrivate.parseTrashInfoFiles,
      entries.map(e => unwrapEntry(e)));
}

export async function getMimeType(entry: Entry|FilesAppEntry) {
  return chrome.fileManagerPrivate.getMimeType(entry.toURL());
}

export async function getFileTasks(
    entries: Array<Entry|FilesAppEntry>, dlpSourceUrls: string[]) {
  return promisify<chrome.fileManagerPrivate.ResultingTasks>(
      chrome.fileManagerPrivate.getFileTasks, entries.map(e => unwrapEntry(e)),
      dlpSourceUrls);
}

export async function executeTask(
    taskDescriptor: chrome.fileManagerPrivate.FileTaskDescriptor,
    entries: Array<Entry|FilesAppEntry>) {
  return promisify<chrome.fileManagerPrivate.TaskResult>(
      chrome.fileManagerPrivate.executeTask, taskDescriptor,
      entries.map(e => unwrapEntry(e)));
}

/**
 * Returns unique parent directories of provided entries. Note: this assumes
 * all provided entries are from the same filesystem.
 */
export async function getUniqueParents(entries: Entry[]):
    Promise<DirectoryEntry[]> {
  if (entries.length === 0) {
    return [];
  }
  const root = entries[0]!.filesystem.root;

  const uniquePaths = entries.reduce((paths: Set<string>, entry: Entry) => {
    const parts = entry.fullPath.split('/').slice(0, -1);

    while (parts.length > 1) {
      const path = parts.join('/');
      if (paths.has(path)) {
        return paths;
      }
      paths.add(path);
      parts.pop();
    }

    return paths;
  }, new Set());

  return Promise.all([...uniquePaths].map(
      (path: string) => getDirectory(root, path, undefined)));
}

/**
 * Gets the current bulk pin progress status.
 */
export async function getBulkPinProgress() {
  return promisify<chrome.fileManagerPrivate.BulkPinProgress>(
      chrome.fileManagerPrivate.getBulkPinProgress);
}

/**
 * Starts calculating the required space to pin all the users items on their My
 * drive.
 */
export async function calculateBulkPinRequiredSpace() {
  return promisify<void>(
      chrome.fileManagerPrivate.calculateBulkPinRequiredSpace);
}

/**
 * Wrap the chrome.fileManagerPrivate.getDriveConnectionStatus function in an
 * async/await compatible style.
 */
export async function getDriveConnectionState() {
  return promisify<chrome.fileManagerPrivate.DriveConnectionState>(
      chrome.fileManagerPrivate.getDriveConnectionState);
}

export async function grantAccess(entries: string[]) {
  return promisify<void>(chrome.fileManagerPrivate.grantAccess, entries);
}

export async function getContentMimeType(fileEntry: FileEntry) {
  return promisify<string>(
      chrome.fileManagerPrivate.getContentMimeType, fileEntry);
}

export async function getContentMetadata(
    fileEntry: FileEntry, mimeType: string, includeImages: boolean) {
  return promisify<chrome.fileManagerPrivate.MediaMetadata>(
      chrome.fileManagerPrivate.getContentMetadata, fileEntry, mimeType,
      includeImages);
}

export async function getEntryProperties(
    entries: Array<Entry|FilesAppEntry>,
    properties: chrome.fileManagerPrivate.EntryPropertyName[]):
    Promise<chrome.fileManagerPrivate.EntryProperties[]> {
  return promisify(
      chrome.fileManagerPrivate.getEntryProperties, entries.map(unwrapEntry),
      properties);
}

export async function getMaterializedViews():
    Promise<chrome.fileManagerPrivate.MaterializedView[]> {
  try {
    const views = await chrome.fileManagerPrivate.getMaterializedViews();
    return views;
  } catch (error: any) {
    console.warn(error);
    return [];
  }
}

export async function readMaterializedView(fileKey: FileKey): Promise<Entry[]> {
  const url = new URL(fileKey);
  const viewId = parseInt(url.pathname.replace('//', '').split('/')[0]!);
  const entryData =
      await chrome.fileManagerPrivate.readMaterializedView(viewId);
  const entries = await Promise.all(entryData.map(e => urlToEntry(e.entryUrl)));
  return entries;
}