| // 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. |
| |
| // Custom binding for the fileBrowserHandler API. |
| |
| const fileBrowserNatives = requireNative('file_browser_handler'); |
| const fileSystemHelpers = requireNative('file_system_natives'); |
| const entryIdManager = require('entryIdManager'); |
| |
| const GetExternalFileEntry = fileBrowserNatives.GetExternalFileEntry; |
| const GetIsolatedFileSystem = fileSystemHelpers.GetIsolatedFileSystem; |
| |
| /** |
| * Adapter to get a FileEntry or DirectoryEntry from |item| passed from API |
| * callback, via calls to GetExternalFileEntry() or get{Directory,File}(). |
| * Ideally we'd use Promise.allSettled() to process multiple files, but since |
| * $Promise.allSettled() does not exist, so callbacks are used instead. |
| * @param {function(!Entry): void} resolve Receiver for resulting Entry. |
| * @param {function(string): void} reject Receiver for error message. |
| * @param {boolean} canCreate For getFile() flow only: Whether to grant |
| * permission to create file if it's missing. Side effect: The file gets |
| * created if missing. |
| * @param {!Object} item Key-value pair passed from API callback, containing |
| * data for GetExternalFileEntry() or get{Directory,File}() flows. |
| */ |
| function GetFileEntry(resolve, reject, canCreate, item) { |
| if (item.hasOwnProperty('fileSystemName')) { |
| // Legacy flow for Ash. Errors (such as nonexistent file) are not detected |
| // here. These only arise downstream when the resulting Entry gets used. |
| resolve(GetExternalFileEntry(item)); |
| } else if (item.hasOwnProperty('fileSystemId')) { |
| // New flow for Lacros. Some errors (such as nonexistent file) are detected |
| // here, and require handling. |
| const fs = GetIsolatedFileSystem(item.fileSystemId); |
| if (item.isDirectory) { |
| fs.root.getDirectory(item.baseName, {}, (dirEntry) => { |
| entryIdManager.registerEntry(item.entryId, dirEntry); |
| resolve(dirEntry); |
| }, (err) => { |
| reject(err.message); |
| }); |
| } else { |
| fs.root.getFile( |
| item.baseName, canCreate ? {create: true} : {}, |
| (fileEntry) => { |
| entryIdManager.registerEntry(item.entryId, fileEntry); |
| resolve(fileEntry); |
| }, |
| (err) => { |
| reject(err.message); |
| }); |
| } |
| } else { |
| reject('Unknown file entry object.'); |
| } |
| } |
| |
| bindingUtil.registerEventArgumentMassager('fileBrowserHandler.onExecute', |
| function(args, dispatch) { |
| if (args.length < 2) { |
| dispatch(args); |
| return; |
| } |
| // The second param for this event's payload is file definition dictionary. |
| const fileList = args[1].entries; |
| if (!fileList) { |
| dispatch(args); |
| return; |
| } |
| |
| // Construct File API's Entry instances. $Promise.allSettled() is unavailable, |
| // so use a |barrier| counter and explicitly sort results. |
| const results = []; |
| let barrier = fileList.length; |
| const onFinish = () => { |
| results.sort((a, b) => a.key - b.key); |
| args[1].entries = $Array.map(results, item => item.entry); |
| dispatch(args); |
| }; |
| const onResolve = (index, entry) => { |
| results.push({key: index, entry}); |
| if (--barrier === 0) onFinish(); |
| }; |
| const onReject = (message) => { |
| console.error(message); |
| if (--barrier === 0) onFinish(); |
| }; |
| for (let i = 0; i < fileList.length; ++i) { |
| GetFileEntry( |
| onResolve.bind(null, i), onReject, |
| /*canCreate*/ false, /*item*/ fileList[i]); |
| } |
| }); |