Commit 3a30346d authored by Jérémie Boulic's avatar Jérémie Boulic Committed by Commit Bot

Extract files_app_entry_types interfaces

Moving interfaces definitions in files_app_entry_types.js to
ui/file_manager/externs/files_app_entry_interfaces.js.

This is part of the preparation for the migration of Files app JS code
to JS modules.  Due to Closure compiler limitations with externs and
JS Modules we'll generate modules for some externs files and use as
modules instead of externs.

This fixes a circular dependency where the implementations in
files_app_entry_types.js depend on volume_info.js which depends on these
extracted interfaces.

BUG=chromium:1133186, b/172300267

Change-Id: If2643c1bc50732c847d0dc99ae19bc4918c5be3b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2517186Reviewed-by: default avatarLuciano Pacheco <lucmult@chromium.org>
Commit-Queue: Jeremie Boulic <jboulic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#823904}
parent 9c36b526
......@@ -24,6 +24,7 @@ js_library("volume_manager") {
# depend on volume_manager_types.js as well, but that's not an extern.
externs_list = [
"entry_location.js",
"files_app_entry_interfaces.js",
"volume_info.js",
"volume_info_list.js",
"volume_manager.js",
......
// Copyright 2020 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.
/**
* @fileoverview Interfaces for the Files app Entry Types.
*/
// clang-format off
// #import * as wrappedVolumeManagerCommon from '../base/js/volume_manager_types.m.js'; const {VolumeManagerCommon} = wrappedVolumeManagerCommon;
// clang-format on
/**
* FilesAppEntry represents a single Entry (file, folder or root) in the Files
* app. Previously, we used the Entry type directly, but this limits the code to
* only work with native Entry type which can't be instantiated in JS.
* For now, Entry and FilesAppEntry should be used interchangeably.
* See also FilesAppDirEntry for a folder-like interface.
*
* TODO(lucmult): Replace uses of Entry with FilesAppEntry implementations.
*
* @interface
*/
/* #export */ class FilesAppEntry {
constructor() {
/**
* @public {boolean} true if this entry represents a Directory-like entry,
* as in have sub-entries and implements {createReader} method.
* This attribute is defined on Entry.
*/
this.isDirectory = false;
/**
* @public {boolean} true if this entry represents a File-like entry.
* Implementations of FilesAppEntry are expected to have this as |true|.
* Whereas implementations of FilesAppDirEntry are expected to have this as
* |false|.
* This attribute is defined on Entry.
*/
this.isFile = true;
/**
* @public {string} absolute path from the file system's root to the entry.
* It can also be thought of as a path which is relative to the root
* directory, prepended with a "/" character.
* This attribute is defined on Entry.
*/
this.fullPath = '';
/**
* @public {string} the name of the entry (the final part of the path,
* after the last.
* This attribute is defined on Entry.
*/
this.name = '';
/**
* @public {string} the class name for this class. It's workaround for the
* fact that an instance created on foreground page and sent to background
* page can't be checked with "instanceof".
*/
this.type_name = 'FilesAppEntry';
/** @public {VolumeManagerCommon.RootType|null} */
this.rootType = null;
}
/**
* @param {function(Entry)|function(FilesAppEntry)} success callback.
* @param {function(Entry)|function(FilesAppEntry)} error callback.
* This method is defined on Entry.
*/
getParent(success, error) {}
/**
* @return {string} used to compare entries. It should return an unique
* identifier for such entry, usually prefixed with it's root type like:
* "fake-entry://unique/path/to/entry".
* This method is defined on Entry.
*/
toURL() {}
/**
* Return metadata via |success| callback. Relevant metadata are
* "modificationTime" and "contentMimeType".
* @param {function(Object)} success callback to be called with the result
* metadata.
* @param {function(Object)} error callback to be called in case of error or
* ignored if no error happened.
*/
getMetadata(success, error) {}
/**
* Returns true if this entry object has a native representation such as Entry
* or DirectoryEntry, this means it can interact with VolumeManager.
* @return {boolean}
*/
get isNativeType() {}
/**
* Returns a FileSystemEntry if this instance has one, returns null if it
* doesn't have or the entry hasn't been resolved yet. It's used to unwrap a
* FilesAppEntry to be able to send to FileSystem API or fileManagerPrivate.
* @return {Entry}
*/
getNativeEntry() {}
}
/**
* Interface with minimal API shared among different types of FilesAppDirEntry
* and native DirectoryEntry. UI components should be able to display any
* implementation of FilesAppEntry.
*
* FilesAppDirEntry represents a DirectoryEntry-like (folder or root) in the
* Files app. It's a specialization of FilesAppEntry extending the behavior for
* folder, which is basically the method createReader.
* As in FilesAppEntry, FilesAppDirEntry should be interchangeable with Entry
* and DirectoryEntry.
*
* @interface
*/
/* #export */ class FilesAppDirEntry extends FilesAppEntry {
constructor() {
super();
/**
* @public {boolean} true if this entry represents a Directory-like entry,
* as in have sub-entries and implements {createReader} method.
* Implementations of FilesAppEntry are expected to have this as |true|.
* This attribute is defined on Entry.
*/
this.isDirectory = true;
this.type_name = 'FilesAppDirEntry';
}
/**
* @return {!DirectoryReader} Returns a reader compatible with
* DirectoryEntry.createReader (from Web Standards) that reads the children of
* this instance.
* This method is defined on DirectoryEntry.
*/
createReader() {}
}
/**
* FakeEntry is used for entries that used only for UI, that weren't generated
* by FileSystem API, like Drive, Downloads or Provided.
*
* @interface
*/
/* #export */ class FakeEntry extends FilesAppDirEntry {
/**
* @param {string} label Translated text to be displayed to user.
* @param {!VolumeManagerCommon.RootType} rootType Root type of this entry.
* @param {chrome.fileManagerPrivate.SourceRestriction=} opt_sourceRestriction
* used on Recents to filter the source of recent files/directories.
* @param {chrome.fileManagerPrivate.RecentFileType=} opt_recentFileType
* used on Recents to filter recent files by their file types.
*/
constructor(label, rootType, opt_sourceRestriction, opt_recentFileType) {
super();
/**
* @public {string} label: Label to be used when displaying to user, it
* should be already translated.
*/
this.label;
/** @public {string} Name for this volume. */
this.name;
/** @public {!VolumeManagerCommon.RootType} */
this.rootType;
/** @public {boolean} true FakeEntry are always directory-like. */
this.isDirectory = true;
/** @public {boolean} false FakeEntry are always directory-like. */
this.isFile = false;
/**
* @public {chrome.fileManagerPrivate.SourceRestriction|undefined} It's used
* to communicate restrictions about sources to
* chrome.fileManagerPrivate.getRecentFiles API.
*/
this.sourceRestriction;
/**
* @public {chrome.fileManagerPrivate.RecentFileType|undefined} It's used to
* communicate file-type filter to chrome.fileManagerPrivate.getRecentFiles
* API.
*/
this.recentFileType;
/**
* @public {string} the class name for this class. It's workaround for the
* fact that an instance created on foreground page and sent to background
* page can't be checked with "instanceof".
*/
this.type_name = 'FakeEntry';
}
/**
* String used to determine the icon.
* @return {string}
*/
get iconName() {}
}
......@@ -69,12 +69,12 @@ class VolumeInfoImpl {
if (volumeType === VolumeManagerCommon.VolumeType.DRIVE) {
this.fakeEntries_[VolumeManagerCommon.RootType.DRIVE_OFFLINE] =
new FakeEntry(
new FakeEntryImpl(
str('DRIVE_OFFLINE_COLLECTION_LABEL'),
VolumeManagerCommon.RootType.DRIVE_OFFLINE);
this.fakeEntries_[VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME] =
new FakeEntry(
new FakeEntryImpl(
str('DRIVE_SHARED_WITH_ME_COLLECTION_LABEL'),
VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME);
}
......
......@@ -47,7 +47,10 @@ js_library("async_util") {
js_library("files_app_entry_types") {
deps = [ "//ui/file_manager/externs:file_manager_private" ]
externs_list = [ "//ui/file_manager/externs/volume_info.js" ]
externs_list = [
"//ui/file_manager/externs/files_app_entry_interfaces.js",
"//ui/file_manager/externs/volume_info.js",
]
}
js_unittest("files_app_entry_types_unittest") {
......
......@@ -21,101 +21,7 @@
* https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryReader
*/
/**
* FilesAppEntry represents a single Entry (file, folder or root) in the Files
* app. Previously, we used the Entry type directly, but this limits the code to
* only work with native Entry type which can't be instantiated in JS.
* For now, Entry and FilesAppEntry should be used interchangeably.
* See also FilesAppDirEntry for a folder-like interface.
*
* TODO(lucmult): Replace uses of Entry with FilesAppEntry implementations.
*
* @interface
*/
class FilesAppEntry {
constructor() {
/**
* @public {boolean} true if this entry represents a Directory-like entry,
* as in have sub-entries and implements {createReader} method.
* This attribute is defined on Entry.
*/
this.isDirectory = false;
/**
* @public {boolean} true if this entry represents a File-like entry.
* Implementations of FilesAppEntry are expected to have this as |true|.
* Whereas implementations of FilesAppDirEntry are expected to have this as
* |false|.
* This attribute is defined on Entry.
*/
this.isFile = true;
/**
* @public {string} absolute path from the file system's root to the entry.
* It can also be thought of as a path which is relative to the root
* directory, prepended with a "/" character.
* This attribute is defined on Entry.
*/
this.fullPath = '';
/**
* @public {string} the name of the entry (the final part of the path,
* after the last.
* This attribute is defined on Entry.
*/
this.name = '';
/**
* @public {string} the class name for this class. It's workaround for the
* fact that an instance created on foreground page and sent to background
* page can't be checked with "instanceof".
*/
this.type_name = 'FilesAppEntry';
/** @public {VolumeManagerCommon.RootType|null} */
this.rootType = null;
}
/**
* @param {function(Entry)|function(FilesAppEntry)} success callback.
* @param {function(Entry)|function(FilesAppEntry)} error callback.
* This method is defined on Entry.
*/
getParent(success, error) {}
/**
* @return {string} used to compare entries. It should return an unique
* identifier for such entry, usually prefixed with it's root type like:
* "fake-entry://unique/path/to/entry".
* This method is defined on Entry.
*/
toURL() {}
/**
* Return metadata via |success| callback. Relevant metadata are
* "modificationTime" and "contentMimeType".
* @param {function(Object)} success callback to be called with the result
* metadata.
* @param {function(Object)} error callback to be called in case of error or
* ignored if no error happened.
*/
getMetadata(success, error) {}
/**
* Returns true if this entry object has a native representation such as Entry
* or DirectoryEntry, this means it can interact with VolumeManager.
* @return {boolean}
*/
get isNativeType() {}
/**
* Returns a FileSystemEntry if this instance has one, returns null if it
* doesn't have or the entry hasn't been resolved yet. It's used to unwrap a
* FilesAppEntry to be able to send to FileSystem API or fileManagerPrivate.
* @return {Entry}
*/
getNativeEntry() {}
}
/**
* A reader compatible with DirectoryEntry.createReader (from Web Standards)
......@@ -210,40 +116,6 @@ class CombinedReaders {
}
}
/**
* Interface with minimal API shared among different types of FilesAppDirEntry
* and native DirectoryEntry. UI components should be able to display any
* implementation of FilesAppEntry.
*
* FilesAppDirEntry represents a DirectoryEntry-like (folder or root) in the
* Files app. It's a specialization of FilesAppEntry extending the behavior for
* folder, which is basically the method createReader.
* As in FilesAppEntry, FilesAppDirEntry should be interchangeable with Entry
* and DirectoryEntry.
*
* @interface
*/
class FilesAppDirEntry extends FilesAppEntry {
constructor() {
super();
/**
* @public {boolean} true if this entry represents a Directory-like entry,
* as in have sub-entries and implements {createReader} method.
* Implementations of FilesAppEntry are expected to have this as |true|.
* This attribute is defined on Entry.
*/
this.isDirectory = true;
this.type_name = 'FilesAppDirEntry';
}
/**
* @return {!DirectoryReader} Returns a reader compatible with
* DirectoryEntry.createReader (from Web Standards) that reads the children of
* this instance.
* This method is defined on DirectoryEntry.
*/
createReader() {}
}
/**
* EntryList, a DirectoryEntry-like object that contains entries. Initially used
......@@ -575,7 +447,7 @@ class VolumeEntry {
* @return {string}
*/
get iconName() {
return this.volumeInfo_.volumeType;
return /** @type {string} */ (this.volumeInfo_.volumeType);
}
/**
......@@ -715,9 +587,9 @@ class VolumeEntry {
* FakeEntry is used for entries that used only for UI, that weren't generated
* by FileSystem API, like Drive, Downloads or Provided.
*
* @implements FilesAppDirEntry
* @implements FakeEntry
*/
class FakeEntry {
class FakeEntryImpl {
/**
* @param {string} label Translated text to be displayed to user.
* @param {!VolumeManagerCommon.RootType} rootType Root type of this entry.
......
......@@ -20,7 +20,7 @@ function fakeVolumeEntry(volumeType, displayRoot, additionalProperties) {
if (displayRoot === undefined) {
displayRoot = createFakeDisplayRoot();
}
let fakeVolumeInfo = {
const fakeVolumeInfo = {
displayRoot: displayRoot,
label: kLabel,
volumeType: volumeType
......@@ -548,7 +548,8 @@ function testEntryListAddEntrySetsPrefix() {
* Test FakeEntry, which is only static data.
*/
function testFakeEntry(testReportCallback) {
let fakeEntry = new FakeEntry('label', VolumeManagerCommon.RootType.CROSTINI);
let fakeEntry =
new FakeEntryImpl('label', VolumeManagerCommon.RootType.CROSTINI);
assertEquals(undefined, fakeEntry.sourceRestriction);
assertEquals('FakeEntry', fakeEntry.type_name);
......@@ -565,7 +566,7 @@ function testFakeEntry(testReportCallback) {
// Check sourceRestriction constructor args.
const kSourceRestriction =
/** @type{chrome.fileManagerPrivate.SourceRestriction} */ ('fake');
fakeEntry = new FakeEntry(
fakeEntry = new FakeEntryImpl(
'label', VolumeManagerCommon.RootType.CROSTINI, kSourceRestriction);
assertEquals(kSourceRestriction, fakeEntry.sourceRestriction);
......
......@@ -94,8 +94,8 @@ function testIsDescendantEntry() {
const file = fileSystem.entries['/file_a.txt'];
const deepFile = fileSystem.entries['/dir_a/dir_b/dir_c/file_g.txt'];
const fakeEntry =
new FakeEntry('fake-entry-label', VolumeManagerCommon.RootType.CROSTINI);
const fakeEntry = new FakeEntryImpl(
'fake-entry-label', VolumeManagerCommon.RootType.CROSTINI);
const entryList =
new EntryList('entry-list-label', VolumeManagerCommon.RootType.MY_FILES);
......@@ -155,8 +155,8 @@ function testEntryDebugString() {
const root = fileSystem.root;
const folder = fileSystem.entries['/dir_a'];
const file = fileSystem.entries['/file_a.txt'];
const fakeEntry =
new FakeEntry('fake-entry-label', VolumeManagerCommon.RootType.CROSTINI);
const fakeEntry = new FakeEntryImpl(
'fake-entry-label', VolumeManagerCommon.RootType.CROSTINI);
const entryList =
new EntryList('entry-list-label', VolumeManagerCommon.RootType.MY_FILES);
entryList.addEntry(fakeEntry);
......@@ -184,7 +184,8 @@ function testEntryDebugString() {
util.entryDebugString(file));
// FilesAppEntry types:
assertEquals(
'(FakeEntry) / fake-entry://crostini', util.entryDebugString(fakeEntry));
'(FakeEntryImpl) / fake-entry://crostini',
util.entryDebugString(fakeEntry));
assertEquals(
'(EntryList) / entry-list://my_files', util.entryDebugString(entryList));
assertEquals(
......
......@@ -44,7 +44,7 @@ class CrostiniController {
this.crostini_.isEnabled(constants.DEFAULT_CROSTINI_VM) ?
new NavigationModelFakeItem(
str('LINUX_FILES_ROOT_LABEL'), NavigationModelItemType.CROSTINI,
new FakeEntry(
new FakeEntryImpl(
str('LINUX_FILES_ROOT_LABEL'),
VolumeManagerCommon.RootType.CROSTINI)) :
null;
......
......@@ -1061,7 +1061,7 @@ class FileManager extends cr.EventTarget {
this.launchParams_.showAndroidPickerApps,
this.launchParams_.includeAllFiles, this.launchParams_.typeList);
this.recentEntry_ = new FakeEntry(
this.recentEntry_ = new FakeEntryImpl(
str('RECENT_ROOT_LABEL'), VolumeManagerCommon.RootType.RECENT,
this.getSourceRestriction_());
......@@ -1545,7 +1545,7 @@ class FileManager extends cr.EventTarget {
if (!this.fakeDriveItem_) {
this.fakeDriveItem_ = new NavigationModelFakeItem(
str('DRIVE_DIRECTORY_LABEL'), NavigationModelItemType.DRIVE,
new FakeEntry(
new FakeEntryImpl(
str('DRIVE_DIRECTORY_LABEL'),
VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT));
}
......
......@@ -68,7 +68,7 @@ function setUp() {
// Create FileTypeFiltersController instance with dependencies.
container = /** @type {!HTMLInputElement} */ (document.createElement('div'));
directoryModel = MockDirectoryModel.create();
recentEntry = new FakeEntry(
recentEntry = new FakeEntryImpl(
'Recent', VolumeManagerCommon.RootType.RECENT,
chrome.fileManagerPrivate.SourceRestriction.ANY_SOURCE);
fileTypeFiltersController =
......
......@@ -553,7 +553,7 @@ class NavigationListModel extends cr.EventTarget {
*/
const createFilteredRecentModelItem = (label, fileType, rootType) => {
const entry = /** @type {!FakeEntry} */ (Object.assign(
Object.create(FakeEntry.prototype), this.recentModelItem_.entry));
Object.create(FakeEntryImpl.prototype), this.recentModelItem_.entry));
entry.recentFileType = fileType;
entry.rootType = rootType;
return new NavigationModelFakeItem(
......
......@@ -77,7 +77,7 @@ function testModel() {
const crostiniFakeItem = new NavigationModelFakeItem(
'linux-files-label', NavigationModelItemType.CROSTINI,
new FakeEntry(
new FakeEntryImpl(
'linux-files-label', VolumeManagerCommon.RootType.CROSTINI));
const androidAppListModelWithApps =
......@@ -515,7 +515,7 @@ function testMyFilesVolumeEnabled(callback) {
const crostiniFakeItem = new NavigationModelFakeItem(
'linux-files-label', NavigationModelItemType.CROSTINI,
new FakeEntry(
new FakeEntryImpl(
'linux-files-label', VolumeManagerCommon.RootType.CROSTINI));
// Navigation items built above:
......
......@@ -117,9 +117,9 @@ function testMultipleSelectionWithKeyboard() {
// Add FileTableList file entries, then draw and focus the table list.
const entries = [
new FakeEntry('entry1-label', VolumeManagerCommon.RootType.CROSTINI),
new FakeEntry('entry2-label', VolumeManagerCommon.RootType.CROSTINI),
new FakeEntry('entry3-label', VolumeManagerCommon.RootType.CROSTINI),
new FakeEntryImpl('entry1-label', VolumeManagerCommon.RootType.CROSTINI),
new FakeEntryImpl('entry2-label', VolumeManagerCommon.RootType.CROSTINI),
new FakeEntryImpl('entry3-label', VolumeManagerCommon.RootType.CROSTINI),
];
const dataModel = new FileListModel(metadataModel);
dataModel.splice(0, 0, ...entries);
......@@ -248,9 +248,9 @@ function testKeyboardOperations() {
// Add FileTableList file entries, then draw and focus the table list.
const entries = [
new FakeEntry('entry1-label', VolumeManagerCommon.RootType.CROSTINI),
new FakeEntry('entry2-label', VolumeManagerCommon.RootType.CROSTINI),
new FakeEntry('entry3-label', VolumeManagerCommon.RootType.CROSTINI),
new FakeEntryImpl('entry1-label', VolumeManagerCommon.RootType.CROSTINI),
new FakeEntryImpl('entry2-label', VolumeManagerCommon.RootType.CROSTINI),
new FakeEntryImpl('entry3-label', VolumeManagerCommon.RootType.CROSTINI),
];
const dataModel = new FileListModel(metadataModel);
dataModel.splice(0, 0, ...entries);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment