910e62b5创建于 1月15日历史提交
// 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]);
      }
    });