[Files F2] Convert file_manager_base to TS

* Remove file_manager_base extern.
* Remove foreground_window extern, the definition is moved to
  file_manager.d.ts which can be recognized by both normal code
  and the test code.
* Update all ".then()" style to use async/await.
* Remove unused event check in the event handler.

Bug: b:289003444
Change-Id: Ie78fbc2afd08619a30aceb2e48142c80db65af1f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5076335
Reviewed-by: Luciano Pacheco <lucmult@chromium.org>
Commit-Queue: Wenbo Jie <wenbojie@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1232551}
diff --git a/ui/file_manager/file_manager/background/js/file_manager_base.js b/ui/file_manager/file_manager/background/js/file_manager_base.js
deleted file mode 100644
index c13258b..0000000
--- a/ui/file_manager/file_manager/background/js/file_manager_base.js
+++ /dev/null
@@ -1,433 +0,0 @@
-// 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.
-import {assert} from 'chrome://resources/ash/common/assert.js';
-import {loadTimeData} from 'chrome://resources/ash/common/load_time_data.m.js';
-
-import {resolveIsolatedEntries} from '../../common/js/api.js';
-import {FilesAppState} from '../../common/js/files_app_state.js';
-import {recordInterval} from '../../common/js/metrics.js';
-import {doIfPrimaryContext} from '../../common/js/util.js';
-import {createArchiveOpenedEvent, VOLUME_ALREADY_MOUNTED, VolumeError, VolumeType} from '../../common/js/volume_manager_types.js';
-import {Crostini} from '../../externs/background/crostini.js';
-import {DriveSyncHandler} from '../../externs/background/drive_sync_handler.js';
-import {FileManagerBaseInterface} from '../../externs/background/file_manager_base.js';
-import {ProgressCenter} from '../../externs/background/progress_center.js';
-
-import {CrostiniImpl} from './crostini.js';
-import {DriveSyncHandlerImpl} from './drive_sync_handler.js';
-import {FileOperationHandler} from './file_operation_handler.js';
-import {launchFileManager, setInitializationPromise} from './launcher.js';
-import {ProgressCenterImpl} from './progress_center.js';
-import {volumeManagerFactory} from './volume_manager_factory.js';
-
-/**
- * Root class of the former background page.
- * @implements {FileManagerBaseInterface}
- */
-export class FileManagerBase {
-  constructor() {
-    /**
-     * Map of all currently open file dialogs. The key is an app ID.
-     * @type {!Record<string, !Window>}
-     */
-    this.dialogs = {};
-
-    /**
-     * Initializes the strings. This needs for the volume manager.
-     * @type {?Promise<*>}
-     */
-    this.initializationPromise_ = new Promise((fulfill) => {
-      chrome.fileManagerPrivate.getStrings(stringData => {
-        if (chrome.runtime.lastError) {
-          console.error(chrome.runtime.lastError.message);
-          return;
-        }
-        if (!loadTimeData.isInitialized()) {
-          loadTimeData.data = assert(stringData);
-        }
-        fulfill(stringData);
-      });
-    });
-
-    /**
-     * Progress center of the background page.
-     * @type {!ProgressCenter}
-     */
-    this.progressCenter = new ProgressCenterImpl();
-
-    /**
-     * Event handler for progress center.
-     * @private @type {?FileOperationHandler}
-     */
-    this.fileOperationHandler_ = null;
-
-    /**
-     * Drive sync handler.
-     * @type {!DriveSyncHandler}
-     */
-    this.driveSyncHandler = /** @type {!DriveSyncHandler}*/ (
-        new DriveSyncHandlerImpl(this.progressCenter));
-
-    /** @type {!Crostini} */
-    this.crostini = /** @type {!Crostini} */ (new CrostiniImpl());
-
-    /**
-     * String assets.
-     * @type {?Record<string, string>}
-     */
-    this.stringData = null;
-
-    // Initialize string and volume manager related stuffs.
-    this.initializationPromise_.then(strings => {
-      this.stringData = strings;
-      this.crostini.initEnabled();
-
-      volumeManagerFactory.getInstance().then(volumeManager => {
-        volumeManager.addEventListener(
-            VOLUME_ALREADY_MOUNTED, this.handleViewEvent_.bind(this));
-
-        this.crostini.initVolumeManager(volumeManager);
-      });
-
-      this.fileOperationHandler_ =
-          new FileOperationHandler(this.progressCenter);
-    });
-
-    // Handle newly mounted FSP file systems. Workaround for crbug.com/456648.
-    // TODO(mtomasz): Replace this hack with a proper solution.
-    chrome.fileManagerPrivate.onMountCompleted.addListener(
-        this.onMountCompleted_.bind(this));
-
-    setInitializationPromise(this.initializationPromise_);
-  }
-
-  /**
-   * @return {!Promise<!import('../../externs/volume_manager.js').VolumeManager>}
-   */
-  async getVolumeManager() {
-    return volumeManagerFactory.getInstance();
-  }
-
-  /**
-   * Register callback to be invoked after initialization.
-   * If the initialization is already done, the callback is invoked immediately.
-   *
-   * @param {function():void} callback Initialize callback to be registered.
-   */
-  ready(callback) {
-    // @ts-ignore: error TS2531: Object is possibly 'null'.
-    this.initializationPromise_.then(callback);
-  }
-
-  /**
-   * Registers dialog window to the background page.
-   *
-   * @param {!Window} dialogWindow Window of the dialog.
-   */
-  registerDialog(dialogWindow) {
-    const id = DIALOG_ID_PREFIX + (nextFileManagerDialogID++);
-    this.dialogs[id] = dialogWindow;
-    if (window.IN_TEST) {
-      dialogWindow.IN_TEST = true;
-    }
-    dialogWindow.addEventListener('pagehide', () => {
-      delete this.dialogs[id];
-    });
-  }
-
-  /**
-   * Launches a new File Manager window.
-   *
-   * @param {!FilesAppState=} appState App state.
-   * @return {!Promise<void>} Resolved when the new window is opened.
-   */
-  // @ts-ignore: error TS2739: Type '{}' is missing the following properties
-  // from type 'FilesAppState': currentDirectoryURL, selectionURL
-  async launchFileManager(appState = {}) {
-    return launchFileManager(appState);
-  }
-
-  /**
-   * Opens the volume root (or opt directoryPath) in main UI.
-   *
-   * @param {!Event} event An event with the volumeId or
-   *     devicePath.
-   * @private
-   */
-  handleViewEvent_(event) {
-    doIfPrimaryContext(() => {
-      this.handleViewEventInternal_(event);
-    });
-  }
-
-  /**
-   * @param {!Event} event An event with the volumeId or
-   *     devicePath.
-   * @private
-   */
-  handleViewEventInternal_(event) {
-    volumeManagerFactory.getInstance().then(
-        /**
-         * Retrieves the root file entry of the volume on the requested
-         * device.
-         * @param {!import('../../externs/volume_manager.js').VolumeManager}
-         *     volumeManager
-         */
-        volumeManager => {
-          // @ts-ignore: error TS2339: Property 'devicePath' does not exist on
-          // type 'Event'.
-          if (event.devicePath) {
-            // @ts-ignore: error TS2339: Property 'devicePath' does not exist on
-            // type 'Event'.
-            const volume = volumeManager.findByDevicePath(event.devicePath);
-            if (volume) {
-              this.navigateToVolumeRoot_(volume);
-            } else {
-              console.warn(
-                  // @ts-ignore: error TS2339: Property 'devicePath' does not
-                  // exist on type 'Event'.
-                  `Got view event with invalid volume id: ${event.devicePath}`);
-            }
-            // @ts-ignore: error TS2339: Property 'volumeId' does not exist on
-            // type 'Event'.
-          } else if (event.volumeId) {
-            if (event.type === VOLUME_ALREADY_MOUNTED) {
-              // @ts-ignore: error TS2339: Property 'volumeId' does not exist on
-              // type 'Event'.
-              this.navigateToVolumeInFocusedWindowWhenReady_(event.volumeId);
-            } else {
-              // @ts-ignore: error TS2339: Property 'volumeId' does not exist on
-              // type 'Event'.
-              this.navigateToVolumeWhenReady_(event.volumeId);
-            }
-          } else {
-            console.warn('Got view event with no actionable destination.');
-          }
-        });
-  }
-
-  /**
-   * Retrieves the root file entry of the volume on the requested device.
-   *
-   * @param {!string} volumeId ID of the volume to navigate to.
-   * @return {!Promise<!import("../../externs/volume_info.js").VolumeInfo>}
-   * @private
-   */
-  retrieveVolumeInfo_(volumeId) {
-    // @ts-ignore: error TS2322: Type 'Promise<void | VolumeInfo>' is not
-    // assignable to type 'Promise<VolumeInfo>'.
-    return volumeManagerFactory.getInstance().then(
-        (/**
-          * @param {!import('../../externs/volume_manager.js').VolumeManager}
-          *     volumeManager
-          */
-         (volumeManager) => {
-           return volumeManager.whenVolumeInfoReady(volumeId).catch((e) => {
-             console.warn(
-                 'Unable to find volume for id: ' + volumeId +
-                 '. Error: ' + e.message);
-           });
-         }));
-  }
-
-  /**
-   * Opens the volume root (or opt directoryPath) in main UI.
-   *
-   * @param {!string} volumeId ID of the volume to navigate to.
-   * @param {!string=} opt_directoryPath Optional path to be opened.
-   * @private
-   */
-  navigateToVolumeWhenReady_(volumeId, opt_directoryPath) {
-    this.retrieveVolumeInfo_(volumeId).then(volume => {
-      this.navigateToVolumeRoot_(volume, opt_directoryPath);
-    });
-  }
-
-  /**
-   * Opens the volume root (or opt directoryPath) in the main UI of the focused
-   * window.
-   *
-   * @param {!string} volumeId ID of the volume to navigate to.
-   * @param {!string=} opt_directoryPath Optional path to be opened.
-   * @private
-   */
-  navigateToVolumeInFocusedWindowWhenReady_(volumeId, opt_directoryPath) {
-    this.retrieveVolumeInfo_(volumeId).then(volume => {
-      this.navigateToVolumeInFocusedWindow_(volume, opt_directoryPath);
-    });
-  }
-
-  /**
-   * If a path was specified, retrieve that directory entry,
-   * otherwise return the root entry of the volume.
-   *
-   * @param {!import("../../externs/volume_info.js").VolumeInfo} volume
-   * @param {string=} opt_directoryPath Optional directory path to be opened.
-   * @return {!Promise<!DirectoryEntry>}
-   * @private
-   */
-  retrieveEntryInVolume_(volume, opt_directoryPath) {
-    return volume.resolveDisplayRoot().then(root => {
-      if (opt_directoryPath) {
-        return new Promise(
-            root.getDirectory.bind(root, opt_directoryPath, {create: false}));
-      } else {
-        return Promise.resolve(root);
-      }
-    });
-  }
-
-  /**
-   * Opens the volume root (or opt directoryPath) in main UI.
-   *
-   * @param {!import("../../externs/volume_info.js").VolumeInfo} volume
-   * @param {string=} opt_directoryPath Optional directory path to be opened.
-   * @private
-   */
-  navigateToVolumeRoot_(volume, opt_directoryPath) {
-    this.retrieveEntryInVolume_(volume, opt_directoryPath)
-        .then(
-            /**
-             * Launches app opened on {@code directory}.
-             * @param {DirectoryEntry} directory
-             */
-            directory => {
-              // @ts-ignore: error TS2345: Argument of type '{
-              // currentDirectoryURL: string; }' is not assignable to parameter
-              // of type 'FilesAppState'.
-              launchFileManager({currentDirectoryURL: directory.toURL()});
-            });
-  }
-
-  /**
-   * Opens the volume root (or opt directoryPath) in main UI of the focused
-   * window.
-   *
-   * @param {!import("../../externs/volume_info.js").VolumeInfo} volume
-   * @param {string=} opt_directoryPath Optional directory path to be opened.
-   * @private
-   */
-  navigateToVolumeInFocusedWindow_(volume, opt_directoryPath) {
-    this.retrieveEntryInVolume_(volume, opt_directoryPath)
-        .then(function(directoryEntry) {
-          if (directoryEntry) {
-            volumeManagerFactory.getInstance().then(volumeManager => {
-              volumeManager.dispatchEvent(
-                  createArchiveOpenedEvent(directoryEntry));
-            });
-          }
-        });
-  }
-
-  /**
-   * Handles mounted FSP volumes and fires the Files app. This is a quick fix
-   * for crbug.com/456648.
-   * @param {!Object} event Event details.
-   * @private
-   */
-  onMountCompleted_(event) {
-    doIfPrimaryContext(() => {
-      this.onMountCompletedInternal_(event);
-    });
-  }
-
-  /**
-   * @param {!Object} event Event details.
-   * @private
-   */
-  onMountCompletedInternal_(event) {
-    // @ts-ignore: error TS2339: Property 'status' does not exist on type
-    // 'Object'.
-    const statusOK = event.status === VolumeError.SUCCESS ||
-        // @ts-ignore: error TS2339: Property 'status' does not exist on type
-        // 'Object'.
-        event.status === VolumeError.PATH_ALREADY_MOUNTED;
-    const volumeTypeOK =
-        // @ts-ignore: error TS2339: Property 'volumeMetadata' does not exist on
-        // type 'Object'.
-        event.volumeMetadata.volumeType === VolumeType.PROVIDED &&
-        // @ts-ignore: error TS2339: Property 'volumeMetadata' does not exist on
-        // type 'Object'.
-        event.volumeMetadata.source === Source.FILE;
-    // @ts-ignore: error TS2339: Property 'eventType' does not exist on type
-    // 'Object'.
-    if (event.eventType === 'mount' && statusOK &&
-        // @ts-ignore: error TS2339: Property 'volumeMetadata' does not exist on
-        // type 'Object'.
-        event.volumeMetadata.mountContext === 'user' && volumeTypeOK) {
-      // @ts-ignore: error TS2339: Property 'volumeMetadata' does not exist on
-      // type 'Object'.
-      this.navigateToVolumeWhenReady_(event.volumeMetadata.volumeId);
-    }
-  }
-}
-
-/**
- * @private @type {number} Total number of retries for the resolve entries
- *     below.
- */
-const MAX_RETRIES = 6;
-
-/**
- * Retry the resolveIsolatedEntries() until we get the same number of entries
- * back.
- * @param {!Array<!Entry>} isolatedEntries Entries that need to be resolved.
- * @return {!Promise<!Array<!Entry>>} Promise resolved with the entries
- *   resolved.
- */
-// @ts-ignore: error TS6133: 'retryResolveIsolatedEntries' is declared but its
-// value is never read.
-async function retryResolveIsolatedEntries(isolatedEntries) {
-  let count = 0;
-  let externalEntries = [];
-  // Wait time in milliseconds between attempts. We double this value after
-  // every wait.
-  let waitTime = 25;
-
-  // Total waiting time is ~1.5 second for `waitTime` starting at 25ms and total
-  // of 6 attempts.
-  while (count <= MAX_RETRIES) {
-    externalEntries = await resolveIsolatedEntries(isolatedEntries);
-    if (externalEntries.length >= isolatedEntries.length) {
-      return externalEntries;
-    }
-
-    console.warn(`Failed to resolve, retrying in ${waitTime}ms...`);
-    await new Promise(resolve => setTimeout(resolve, waitTime));
-    waitTime = waitTime * 2;
-    count += 1;
-  }
-
-  console.warn(
-      `Failed to resolve: Requested ${isolatedEntries.length},` +
-      ` resolved: ${externalEntries.length}.`);
-  return [];
-}
-
-/**
- * Prefix for the dialog ID.
- * @type {!string}
- * @const
- */
-const DIALOG_ID_PREFIX = 'dialog#';
-
-/**
- * Value of the next file manager dialog ID.
- * @type {number}
- */
-let nextFileManagerDialogID = 0;
-
-/**
- * Singleton instance of Background object.
- * @type {!FileManagerBaseInterface}
- */
-export const background = new FileManagerBase();
-// @ts-ignore: error TS2339: Property 'background' does not exist on type
-// 'Window & typeof globalThis'.
-window.background = background;
-
-/**
- * End recording of the background page Load.BackgroundScript metric.
- */
-recordInterval('Load.BackgroundScript');
diff --git a/ui/file_manager/file_manager/background/js/file_manager_base.ts b/ui/file_manager/file_manager/background/js/file_manager_base.ts
new file mode 100644
index 0000000..5dbcd37
--- /dev/null
+++ b/ui/file_manager/file_manager/background/js/file_manager_base.ts
@@ -0,0 +1,293 @@
+// 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.
+import {loadTimeData} from 'chrome://resources/ash/common/load_time_data.m.js';
+import {assert} from 'chrome://resources/js/assert.js';
+
+import {getDirectory} from '../../common/js/api.js';
+import {FilesAppState} from '../../common/js/files_app_state.js';
+import {recordInterval} from '../../common/js/metrics.js';
+import {isInGuestMode} from '../../common/js/util.js';
+import {createArchiveOpenedEvent, Source, VOLUME_ALREADY_MOUNTED, VolumeError, VolumeType} from '../../common/js/volume_manager_types.js';
+import {ProgressCenter} from '../../externs/background/progress_center.js';
+import type {VolumeInfo} from '../../externs/volume_info.js';
+import type {VolumeManager} from '../../externs/volume_manager.js';
+
+import {AppWindowWrapper} from './app_window_wrapper.js';
+import {CrostiniImpl} from './crostini.js';
+import {DriveSyncHandlerImpl} from './drive_sync_handler.js';
+import {FileOperationHandler} from './file_operation_handler.js';
+import {ProgressCenterImpl} from './progress_center.js';
+import {volumeManagerFactory} from './volume_manager_factory.js';
+import type {VolumeAlreadyMountedEvent} from './volume_manager_impl.js';
+
+/**
+ * Root class of the former background page.
+ */
+export class FileManagerBase {
+  private initializationPromise_: Promise<Record<string, string>>;
+  protected fileOperationHandler_: FileOperationHandler|null = null;
+
+  /**
+   * Map of all currently open file dialogs. The key is an app ID.
+   */
+  dialogs: Record<string, Window> = {};
+
+  /**
+   * Progress center of the background page.
+   */
+  progressCenter: ProgressCenter = new ProgressCenterImpl();
+
+  /**
+   * Drive sync handler.
+   */
+  driveSyncHandler = new DriveSyncHandlerImpl(this.progressCenter);
+
+  crostini = new CrostiniImpl();
+
+  /**
+   * String assets.
+   */
+  stringData: null|Record<string, string> = null;
+
+  constructor() {
+    /**
+     * Initializes the strings. This needs for the volume manager.
+     */
+    this.initializationPromise_ = new Promise((fulfill) => {
+      chrome.fileManagerPrivate.getStrings(stringData => {
+        if (chrome.runtime.lastError) {
+          console.error(chrome.runtime.lastError.message);
+          return;
+        }
+        if (!loadTimeData.isInitialized()) {
+          loadTimeData.data = assert(stringData);
+        }
+        fulfill(stringData as Record<string, string>);
+      });
+    });
+    this.initializationPromise_.then(strings => {
+      this.stringData = strings;
+      this.crostini.initEnabled();
+
+      volumeManagerFactory.getInstance().then(volumeManager => {
+        volumeManager.addEventListener(
+            VOLUME_ALREADY_MOUNTED, this.handleViewEvent_.bind(this));
+
+        this.crostini.initVolumeManager(volumeManager);
+      });
+
+      this.fileOperationHandler_ =
+          new FileOperationHandler(this.progressCenter);
+    });
+
+    // Handle newly mounted FSP file systems. Workaround for crbug.com/456648.
+    // TODO(mtomasz): Replace this hack with a proper solution.
+    chrome.fileManagerPrivate.onMountCompleted.addListener(
+        this.onMountCompleted_.bind(this));
+  }
+
+  async getVolumeManager(): Promise<VolumeManager> {
+    return volumeManagerFactory.getInstance();
+  }
+
+  async ready(): Promise<void> {
+    await this.initializationPromise_;
+  }
+
+  /**
+   * Registers dialog window to the background page.
+   *
+   * @param dialogWindow Window of the dialog.
+   */
+  registerDialog(dialogWindow: Window) {
+    const id = DIALOG_ID_PREFIX + (nextFileManagerDialogID++);
+    this.dialogs[id] = dialogWindow;
+    if (window.IN_TEST) {
+      dialogWindow.IN_TEST = true;
+    }
+    dialogWindow.addEventListener('pagehide', () => {
+      delete this.dialogs[id];
+    });
+  }
+
+  /**
+   * Launches a new File Manager window.
+   *
+   * @param appState App state.
+   * @return Resolved when the new window is opened.
+   */
+  async launchFileManager(appState: FilesAppState = {}): Promise<void> {
+    await this.initializationPromise_;
+
+    const appWindow = new AppWindowWrapper();
+
+    return appWindow.launch(appState || {});
+  }
+
+  /**
+   * Opens the volume root (or opt directoryPath) in main UI.
+   *
+   * @param event An event with the volumeId or
+   *     devicePath.
+   */
+  private async handleViewEvent_(event: Event) {
+    const isPrimaryContext = await isInGuestMode();
+    if (isPrimaryContext) {
+      this.handleViewEventInternal_(event);
+    }
+  }
+
+  /**
+   * @param event An event with the volumeId.
+   */
+  private async handleViewEventInternal_(event: Event): Promise<void> {
+    await volumeManagerFactory.getInstance();
+    // event can only be VolumeAlreadyMountedEvent according to the
+    // addEventListener in the constructor.
+    const volumeAlreadyMountedEvent = event as VolumeAlreadyMountedEvent;
+    this.navigateToVolumeInFocusedWindowWhenReady_(
+        volumeAlreadyMountedEvent.volumeId);
+  }
+
+  /**
+   * Retrieves the root file entry of the volume on the requested device.
+   *
+   * @param volumeId ID of the volume to navigate to.
+   */
+  private async retrieveVolumeInfo_(volumeId: string):
+      Promise<VolumeInfo|void> {
+    const volumeManager = await volumeManagerFactory.getInstance();
+    try {
+      return await volumeManager.whenVolumeInfoReady(volumeId);
+    } catch (e: any) {
+      console.warn(
+          'Unable to find volume for id: ' + volumeId +
+          '. Error: ' + e.message);
+    }
+  }
+
+  /**
+   * Opens the volume root (or opt directoryPath) in main UI.
+   *
+   * @param volumeId ID of the volume to navigate to.
+   * @param directoryPath Optional path to be opened.
+   */
+  private async navigateToVolumeWhenReady_(
+      volumeId: string, directoryPath?: string): Promise<void> {
+    const volume = await this.retrieveVolumeInfo_(volumeId);
+    if (volume) {
+      this.navigateToVolumeRoot_(volume, directoryPath);
+    }
+  }
+
+  /**
+   * Opens the volume root (or opt directoryPath) in the main UI of the focused
+   * window.
+   *
+   * @param volumeId ID of the volume to navigate to.
+   * @param directoryPath Optional path to be opened.
+   */
+  private async navigateToVolumeInFocusedWindowWhenReady_(
+      volumeId: string, directoryPath?: string): Promise<void> {
+    const volume = await this.retrieveVolumeInfo_(volumeId);
+    if (volume) {
+      this.navigateToVolumeInFocusedWindow_(volume, directoryPath);
+    }
+  }
+
+  /**
+   * If a path was specified, retrieve that directory entry,
+   * otherwise return the root entry of the volume.
+   *
+   * @param directoryPath Optional directory path to be opened.
+   */
+  private async retrieveEntryInVolume_(
+      volume: VolumeInfo, directoryPath?: string): Promise<DirectoryEntry> {
+    const root = await volume.resolveDisplayRoot();
+    if (directoryPath) {
+      return getDirectory(root, directoryPath, {create: false});
+    }
+    return root;
+  }
+
+  /**
+   * Opens the volume root (or opt directoryPath) in main UI.
+   *
+   * @param directoryPath Optional directory path to be opened.
+   */
+  private async navigateToVolumeRoot_(
+      volume: VolumeInfo, directoryPath?: string): Promise<void> {
+    const directory = await this.retrieveEntryInVolume_(volume, directoryPath);
+    /**
+     * Launches app opened on {@code directory}.
+     */
+    this.launchFileManager({currentDirectoryURL: directory.toURL()});
+  }
+
+  /**
+   * Opens the volume root (or opt directoryPath) in main UI of the focused
+   * window.
+   *
+   * @param directoryPath Optional directory path to be opened.
+   */
+  private async navigateToVolumeInFocusedWindow_(
+      volume: VolumeInfo, directoryPath?: string): Promise<void> {
+    const directoryEntry =
+        await this.retrieveEntryInVolume_(volume, directoryPath);
+    if (directoryEntry) {
+      const volumeManager = await volumeManagerFactory.getInstance();
+      volumeManager.dispatchEvent(createArchiveOpenedEvent(directoryEntry));
+    }
+  }
+
+  /**
+   * Handles mounted FSP volumes and fires the Files app. This is a quick fix
+   * for crbug.com/456648.
+   * @param event Event details.
+   */
+  private async onMountCompleted_(
+      event: chrome.fileManagerPrivate.MountCompletedEvent) {
+    const isPrimaryContext = await isInGuestMode();
+    if (isPrimaryContext) {
+      this.onMountCompletedInternal_(event);
+    }
+  }
+
+  /**
+   * @param event Event details.
+   */
+  private onMountCompletedInternal_(
+      event: chrome.fileManagerPrivate.MountCompletedEvent) {
+    const statusOK = event.status === VolumeError.SUCCESS ||
+        event.status === VolumeError.PATH_ALREADY_MOUNTED;
+    const volumeTypeOK =
+        event.volumeMetadata.volumeType === VolumeType.PROVIDED &&
+        event.volumeMetadata.source === Source.FILE;
+    if (event.eventType === 'mount' && statusOK &&
+        event.volumeMetadata.mountContext === 'user' && volumeTypeOK) {
+      this.navigateToVolumeWhenReady_(event.volumeMetadata.volumeId);
+    }
+  }
+}
+
+/**
+ * Prefix for the dialog ID.
+ */
+const DIALOG_ID_PREFIX = 'dialog#';
+
+/**
+ * Value of the next file manager dialog ID.
+ */
+let nextFileManagerDialogID = 0;
+
+/**
+ * Singleton instance of Background object.
+ */
+export const background = new FileManagerBase();
+window.background = background;
+
+/**
+ * End recording of the background page Load.BackgroundScript metric.
+ */
+recordInterval('Load.BackgroundScript');
diff --git a/ui/file_manager/file_manager/background/js/launcher.ts b/ui/file_manager/file_manager/background/js/launcher.ts
deleted file mode 100644
index 1f4ec6d..0000000
--- a/ui/file_manager/file_manager/background/js/launcher.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import type {FilesAppState} from '../../common/js/files_app_state.js';
-
-import {AppWindowWrapper} from './app_window_wrapper.js';
-
-/**
- * Prefix for the file manager window ID.
- */
-const FILES_ID_PREFIX = 'files#';
-
-/**
- * Regexp matching a file manager window ID.
- */
-export const FILES_ID_PATTERN = new RegExp('^' + FILES_ID_PREFIX + '(\\d*)$');
-
-/**
- * Promise to serialize asynchronous calls.
- */
-let initializationPromise: Promise<void>|null = null;
-
-export function setInitializationPromise(promise: Promise<void>) {
-  initializationPromise = promise;
-}
-
-/**
- * The returned promise will be resolved when the window is launched.
- */
-export async function launchFileManager(appState?: FilesAppState):
-    Promise<void> {
-  // Serialize concurrent calls to launchFileManager.
-  if (!initializationPromise) {
-    throw new Error('Missing initializationPromise');
-  }
-
-  await initializationPromise;
-
-  const appWindow = new AppWindowWrapper();
-
-  // TODO: Remove `as FileAppsState` this type is an TS interface.
-  await appWindow.launch(appState || {} as FilesAppState);
-}
diff --git a/ui/file_manager/file_manager/background/js/runtime_loaded_test_util.js b/ui/file_manager/file_manager/background/js/runtime_loaded_test_util.js
index e0bf997..c42f1ea8 100644
--- a/ui/file_manager/file_manager/background/js/runtime_loaded_test_util.js
+++ b/ui/file_manager/file_manager/background/js/runtime_loaded_test_util.js
@@ -14,16 +14,9 @@
 import {entriesToURLs} from '../../common/js/entry_utils.js';
 import {recordEnum} from '../../common/js/metrics.js';
 import {VolumeType} from '../../common/js/volume_manager_types.js';
-import {FileManagerBaseInterface} from '../../externs/background/file_manager_base.js';
 
 import {test} from './test_util_base.js';
 
-
-/** @type {!FileManagerBaseInterface} */
-// @ts-ignore: error TS2339: Property 'background' does not exist on type
-// 'Window & typeof globalThis'.
-window.background;
-
 /**
  * @typedef {{
  *   attributes:Record<string, string>,
diff --git a/ui/file_manager/file_manager/background/js/volume_manager_impl.ts b/ui/file_manager/file_manager/background/js/volume_manager_impl.ts
index cd086454..20fe50f 100644
--- a/ui/file_manager/file_manager/background/js/volume_manager_impl.ts
+++ b/ui/file_manager/file_manager/background/js/volume_manager_impl.ts
@@ -153,7 +153,7 @@
 }
 
 
-type VolumeAlreadyMountedEvent = Event&{
+export type VolumeAlreadyMountedEvent = Event&{
   volumeId: string,
 };
 
diff --git a/ui/file_manager/file_manager/common/js/files_app_state.ts b/ui/file_manager/file_manager/common/js/files_app_state.ts
index 3571d667..03edf27 100644
--- a/ui/file_manager/file_manager/common/js/files_app_state.ts
+++ b/ui/file_manager/file_manager/common/js/files_app_state.ts
@@ -32,31 +32,31 @@
   /**
    * The desired target directory when opening a new window.
    */
-  currentDirectoryURL: string|null|undefined;
+  currentDirectoryURL?: string|null;
 
   /**
    * The URL for a file or directory to be selected once a new window is
    * spawned.
    */
-  selectionURL: string|undefined;
+  selectionURL?: string;
 
   /**
    * For SaveAs dialog it prefills the <input> for the file name with this
    * value.
    * For FilePicker it pre-selects the file in the file list.
    */
-  targetName: string|undefined;
+  targetName?: string;
 
   /**
    * Search term to initialize the Files app directly in a search results.
    */
-  searchQuery: string|undefined;
+  searchQuery?: string;
 
   /**
    * The type of the window being opened, when it's undefined it defaults to
    * the normal Files app window (non-dialog version).
    */
-  type: DialogType|undefined;
+  type?: DialogType;
 
   /**
    * List of file extensions (.txt, .zip, etc) that will be used by
@@ -64,13 +64,13 @@
    * Files app displays Android apps that can handle such extensions in the
    * DirectoryTree.
    */
-  typeList: TypeList[]|undefined;
+  typeList?: TypeList[];
 
   /**
    * For FilePicker indicates that the "All files" should be displayed in the
    * file type dropdown in the footer.
    */
-  includeAllFiles: boolean|undefined;
+  includeAllFiles?: boolean;
 
   /**
    * Defines what volumes are available in the Files app, when NATIVE_PATH is
@@ -78,12 +78,12 @@
    *
    * Defaults to `ANY_PATH_OR_URL` when undefined.
    */
-  allowedPaths: AllowedPaths|undefined;
+  allowedPaths?: AllowedPaths;
 
   /**
    * If the Android apps should be shown in the DirectoryTree for FilePicker.
    */
-  showAndroidPickerApps: boolean|undefined;
+  showAndroidPickerApps?: boolean;
 
   /**
    * Array of Files app mode dependent volume filter names. Defaults to an
@@ -92,5 +92,5 @@
    * See filtered_volume_manager.js for details about the available volume
    * filter names and their volume filter effects.
    */
-  volumeFilter: string[]|undefined;
+  volumeFilter?: string[];
 }
diff --git a/ui/file_manager/file_manager/common/js/util.ts b/ui/file_manager/file_manager/common/js/util.ts
index 4910ccf5..6fced3c 100644
--- a/ui/file_manager/file_manager/common/js/util.ts
+++ b/ui/file_manager/file_manager/common/js/util.ts
@@ -171,21 +171,6 @@
 }
 
 /**
- * Executes a functions only when the context is not the incognito one in a
- * regular session. Returns a promise that when fulfilled informs us whether or
- * not the callback was invoked.
- */
-export async function doIfPrimaryContext(callback: VoidCallback):
-    Promise<boolean> {
-  const guestMode = await isInGuestMode();
-  if (guestMode) {
-    callback();
-    return true;
-  }
-  return false;
-}
-
-/**
  * Returns the Files app modal dialog used to embed any files app dialog
  * that derives from cr.ui.dialogs.
  */
diff --git a/ui/file_manager/file_manager/definitions/file_manager.d.ts b/ui/file_manager/file_manager/definitions/file_manager.d.ts
index de069fb8..5b93f60f 100644
--- a/ui/file_manager/file_manager/definitions/file_manager.d.ts
+++ b/ui/file_manager/file_manager/definitions/file_manager.d.ts
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import type {FileManagerBase} from '../background/js/file_manager_base.js';
 import type {VolumeManager} from '../externs/volume_manager.js';
-import {MetadataModel} from '../foreground/js/metadata/metadata_model.js';
+import type {MetadataModel} from '../foreground/js/metadata/metadata_model.js';
 
 /**
  * Type definition for foreground/js/file_manager.js:FileManager.
@@ -55,6 +56,9 @@
       },
     };
 
+    // Defined in the file_manager_base.ts
+    background: FileManagerBase;
+
     // Defined in the main_window_component.ts
     isFocused?: () => boolean;
   }
diff --git a/ui/file_manager/file_manager/externs/background/file_manager_base.js b/ui/file_manager/file_manager/externs/background/file_manager_base.js
deleted file mode 100644
index f10f18c9..0000000
--- a/ui/file_manager/file_manager/externs/background/file_manager_base.js
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {FilesAppState} from '../../common/js/files_app_state.js';
-
-import {Crostini} from './crostini.js';
-import {DriveSyncHandler} from './drive_sync_handler.js';
-import {ProgressCenter} from './progress_center.js';
-
-/**
- * @interface
- */
-export class FileManagerBaseInterface {
-  constructor() {
-    /** @type {!Record<string, !Window>} */
-    this.dialogs;
-
-    /**
-     * @type {!DriveSyncHandler}
-     */
-    this.driveSyncHandler;
-
-    /**
-     * @type {!ProgressCenter}
-     */
-    this.progressCenter;
-
-    /**
-     * String assets.
-     * @type {?Record<string, string>}
-     */
-    this.stringData;
-
-    /**
-     * @type {!Crostini}
-     */
-    this.crostini;
-  }
-
-  // @ts-ignore: error TS2355: A function whose declared type is neither 'void'
-  // nor 'any' must return a value.
-  /** @return {!Promise<!import('../volume_manager.js').VolumeManager>} */
-  getVolumeManager() {}
-
-  /**
-   * Register callback to be invoked after initialization of the background
-   * page. If the initialization is already done, the callback is invoked
-   * immediately.
-   *
-   * @param {function():void} callback
-   */
-  // @ts-ignore: error TS6133: 'callback' is declared but its value is never
-  // read.
-  ready(callback) {}
-
-  /**
-   * Registers a dialog (file picker or save as) in the background page.
-   * Dialogs are opened by the browser directly and should register themselves
-   * in the background page.
-   * @param {!Window} window
-   */
-  // @ts-ignore: error TS6133: 'window' is declared but its value is never read.
-  registerDialog(window) {}
-
-  /**
-   * Launches a new File Manager window.
-   *
-   * @param {!FilesAppState=} appState App state.
-   * @return {!Promise<void>} Resolved when the new window is opened.
-   */
-  // @ts-ignore: error TS2739: Type '{}' is missing the following properties
-  // from type 'FilesAppState': currentDirectoryURL, selectionURL
-  async launchFileManager(appState = {}) {}
-}
diff --git a/ui/file_manager/file_manager/externs/foreground_window.js b/ui/file_manager/file_manager/externs/foreground_window.js
deleted file mode 100644
index 46fc169d..0000000
--- a/ui/file_manager/file_manager/externs/foreground_window.js
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview External objects and functions required for compiling tests.
- */
-
-import {Crostini} from './background/crostini.js';
-import {FileManagerBaseInterface} from './background/file_manager_base.js';
-
-/** @interface */
-class FileManagerTestDeps {
-  constructor() {
-    /** @type {Crostini} */
-    this.crostini;
-  }
-}
-
-/**
- * @extends {Window}
- */
-export class ForegroundWindow {
-  constructor() {
-    /** @type {FileManagerTestDeps} */
-    this.fileManager;
-
-    /**
-     * @type {!FileManagerBaseInterface}
-     */
-    this.background;
-  }
-
-  // @ts-ignore: error TS2355: A function whose declared type is neither 'void'
-  // nor 'any' must return a value.
-  /** @return {boolean} */
-  isFocused() {}
-}
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js
index d1074dd..b44658b 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -18,6 +18,7 @@
 import {ColorChangeUpdater} from 'chrome://resources/cr_components/color_change_listener/colors_css_updater.js';
 import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
 
+import {FileManagerBase} from '../../background/js/file_manager_base.js';
 import {getBulkPinProgress, getDialogCaller, getDlpBlockedComponents, getDriveConnectionState, getPreferences} from '../../common/js/api.js';
 import {ArrayDataModel} from '../../common/js/array_data_model.js';
 import {isFolderDialogType} from '../../common/js/dialog_type.js';
@@ -35,11 +36,9 @@
 import {DirectoryTreeContainer} from '../../containers/directory_tree_container.js';
 import {NudgeType} from '../../containers/nudge_container.js';
 import {Crostini} from '../../externs/background/crostini.js';
-import {FileManagerBaseInterface} from '../../externs/background/file_manager_base.js';
 import {ProgressCenter} from '../../externs/background/progress_center.js';
 import {CommandHandlerDeps} from '../../externs/command_handler_deps.js';
 import {FakeEntry, FilesAppDirEntry} from '../../externs/files_app_entry_interfaces.js';
-import {ForegroundWindow} from '../../externs/foreground_window.js';
 import {DialogType, PropStatus, SearchLocation} from '../../externs/ts/state.js';
 import {Store} from '../../externs/ts/store.js';
 import {getMyFiles} from '../../state/ducks/all_entries.js';
@@ -344,7 +343,7 @@
     // DOM elements.
 
     /**
-     * @private @type {?FileManagerBaseInterface}
+     * @private @type {?FileManagerBase}
      */
     this.fileBrowserBackground_ = null;
 
@@ -1097,16 +1096,9 @@
   async startInitBackgroundPage_() {
     startInterval('Load.InitBackgroundPage');
 
-    this.fileBrowserBackground_ =
-        // @ts-ignore: error TS2352: Conversion of type 'Window & typeof
-        // globalThis' to type 'ForegroundWindow' may be a mistake because
-        // neither type sufficiently overlaps with the other. If this was
-        // intentional, convert the expression to 'unknown' first.
-        /** @type {!ForegroundWindow} */ (window).background;
+    this.fileBrowserBackground_ = window.background;
 
-    // @ts-ignore: error TS2345: Argument of type '(value: any) => void' is not
-    // assignable to parameter of type '() => void'.
-    await new Promise(resolve => this.fileBrowserBackground_.ready(resolve));
+    await this.fileBrowserBackground_.ready();
 
     // For the SWA, we load background and foreground in the same Window, avoid
     // loading the `data` twice.
diff --git a/ui/file_manager/file_names.gni b/ui/file_manager/file_names.gni
index 7394a3a..e56bfdc 100644
--- a/ui/file_manager/file_names.gni
+++ b/ui/file_manager/file_names.gni
@@ -15,8 +15,6 @@
 
 static_js_files = [
   # Background:
-  "file_manager/background/js/file_manager_base.js",
-
   "file_manager/background/js/runtime_loaded_test_util.js",
   "file_manager/background/js/test_util.js",
 
@@ -31,13 +29,11 @@
   # Externs:
   "file_manager/externs/background/crostini.js",
   "file_manager/externs/background/drive_sync_handler.js",
-  "file_manager/externs/background/file_manager_base.js",
   "file_manager/externs/background/progress_center.js",
   "file_manager/externs/command_handler_deps.js",
   "file_manager/externs/entry_location.js",
   "file_manager/externs/exif_entry.js",
   "file_manager/externs/files_app_entry_interfaces.js",
-  "file_manager/externs/foreground_window.js",
   "file_manager/externs/metadata_model.js",
   "file_manager/externs/progress_center_panel.js",
   "file_manager/externs/volume_info_list.js",
@@ -285,9 +281,9 @@
   "file_manager/background/js/drive_sync_handler.ts",
   "file_manager/background/js/entry_location_impl.ts",
   "file_manager/foreground/js/file_selection.ts",
+  "file_manager/background/js/file_manager_base.ts",
   "file_manager/background/js/file_operation_handler.ts",
   "file_manager/foreground/js/file_watcher.ts",
-  "file_manager/background/js/launcher.ts",
   "file_manager/foreground/js/main_window_component.ts",
   "file_manager/background/js/metrics_start.ts",
   "file_manager/foreground/js/mock_actions_model.ts",