• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Custom binding for the fileSystem API.
6
7var binding = require('binding').Binding.create('fileSystem');
8
9var fileSystemNatives = requireNative('file_system_natives');
10var GetIsolatedFileSystem = fileSystemNatives.GetIsolatedFileSystem;
11var lastError = require('lastError');
12var sendRequest = require('sendRequest');
13var GetModuleSystem = requireNative('v8_context').GetModuleSystem;
14// TODO(sammc): Don't require extension. See http://crbug.com/235689.
15var GetExtensionViews = requireNative('runtime').GetExtensionViews;
16
17// Fallback to using the current window if no background page is running.
18var backgroundPage = GetExtensionViews(-1, 'BACKGROUND')[0] || window;
19var backgroundPageModuleSystem = GetModuleSystem(backgroundPage);
20
21// All windows use the bindFileEntryCallback from the background page so their
22// FileEntry objects have the background page's context as their own. This
23// allows them to be used from other windows (including the background page)
24// after the original window is closed.
25if (window == backgroundPage) {
26  var bindFileEntryCallback = function(functionName, apiFunctions) {
27    apiFunctions.setCustomCallback(functionName,
28        function(name, request, response) {
29      if (request.callback && response) {
30        var callback = request.callback;
31        request.callback = null;
32
33        var entries = [];
34        var hasError = false;
35
36        var getEntryError = function(fileError) {
37          if (!hasError) {
38            hasError = true;
39            lastError.run(
40                'fileSystem.' + functionName,
41                'Error getting fileEntry, code: ' + fileError.code,
42                request.stack,
43                callback);
44          }
45        }
46
47        // Loop through the response entries and asynchronously get the
48        // FileEntry for each. We use hasError to ensure that only the first
49        // error is reported. Note that an error can occur either during the
50        // loop or in the asynchronous error callback to getFile.
51        $Array.forEach(response.entries, function(entry) {
52          if (hasError)
53            return;
54          var fileSystemId = entry.fileSystemId;
55          var baseName = entry.baseName;
56          var id = entry.id;
57          var fs = GetIsolatedFileSystem(fileSystemId);
58
59          try {
60            var getEntryCallback = function(fileEntry) {
61              if (hasError)
62                return;
63              entryIdManager.registerEntry(id, fileEntry);
64              entries.push(fileEntry);
65              // Once all entries are ready, pass them to the callback. In the
66              // event of an error, this condition will never be satisfied so
67              // the callback will not be called with any entries.
68              if (entries.length == response.entries.length) {
69                if (response.multiple) {
70                  sendRequest.safeCallbackApply(
71                      'fileSystem.' + functionName, request, callback,
72                      [entries]);
73                } else {
74                  sendRequest.safeCallbackApply(
75                      'fileSystem.' + functionName, request, callback,
76                      [entries[0]]);
77                }
78              }
79            }
80            // TODO(koz): fs.root.getFile() makes a trip to the browser process,
81            // but it might be possible avoid that by calling
82            // WebDOMFileSystem::createV8Entry().
83            if (entry.isDirectory) {
84              fs.root.getDirectory(baseName, {}, getEntryCallback,
85                                   getEntryError);
86            } else {
87              fs.root.getFile(baseName, {}, getEntryCallback, getEntryError);
88            }
89          } catch (e) {
90            if (!hasError) {
91              hasError = true;
92              lastError.run('fileSystem.' + functionName,
93                            'Error getting fileEntry: ' + e.stack,
94                            request.stack,
95                            callback);
96            }
97          }
98        });
99      }
100    });
101  };
102  var entryIdManager = require('entryIdManager');
103} else {
104  // Force the fileSystem API to be loaded in the background page. Using
105  // backgroundPageModuleSystem.require('fileSystem') is insufficient as
106  // requireNative is only allowed while lazily loading an API.
107  backgroundPage.chrome.fileSystem;
108  var bindFileEntryCallback = backgroundPageModuleSystem.require(
109      'fileSystem').bindFileEntryCallback;
110  var entryIdManager = backgroundPageModuleSystem.require('entryIdManager');
111}
112
113binding.registerCustomHook(function(bindingsAPI) {
114  var apiFunctions = bindingsAPI.apiFunctions;
115  var fileSystem = bindingsAPI.compiledApi;
116
117  function bindFileEntryFunction(functionName) {
118    apiFunctions.setUpdateArgumentsPostValidate(
119        functionName, function(fileEntry, callback) {
120      var fileSystemName = fileEntry.filesystem.name;
121      var relativePath = $String.slice(fileEntry.fullPath, 1);
122      return [fileSystemName, relativePath, callback];
123    });
124  }
125  $Array.forEach(['getDisplayPath', 'getWritableEntry', 'isWritableEntry'],
126                  bindFileEntryFunction);
127
128  $Array.forEach(['getWritableEntry', 'chooseEntry', 'restoreEntry'],
129                  function(functionName) {
130    bindFileEntryCallback(functionName, apiFunctions);
131  });
132
133  apiFunctions.setHandleRequest('retainEntry', function(fileEntry) {
134    var id = entryIdManager.getEntryId(fileEntry);
135    if (!id)
136      return '';
137    var fileSystemName = fileEntry.filesystem.name;
138    var relativePath = $String.slice(fileEntry.fullPath, 1);
139
140    sendRequest.sendRequest(this.name, [id, fileSystemName, relativePath],
141      this.definition.parameters, {});
142    return id;
143  });
144
145  apiFunctions.setHandleRequest('isRestorable',
146      function(id, callback) {
147    var savedEntry = entryIdManager.getEntryById(id);
148    if (savedEntry) {
149      sendRequest.safeCallbackApply(
150          'fileSystem.isRestorable',
151          {'stack': sendRequest.getExtensionStackTrace()},
152          callback,
153          [true]);
154    } else {
155      sendRequest.sendRequest(
156          this.name, [id, callback], this.definition.parameters, {});
157    }
158  });
159
160  apiFunctions.setUpdateArgumentsPostValidate('restoreEntry',
161      function(id, callback) {
162    var savedEntry = entryIdManager.getEntryById(id);
163    if (savedEntry) {
164      // We already have a file entry for this id so pass it to the callback and
165      // send a request to the browser to move it to the back of the LRU.
166      sendRequest.safeCallbackApply(
167          'fileSystem.restoreEntry',
168          {'stack': sendRequest.getExtensionStackTrace()},
169          callback,
170          [savedEntry]);
171      return [id, false, null];
172    } else {
173      // Ask the browser process for a new file entry for this id, to be passed
174      // to |callback|.
175      return [id, true, callback];
176    }
177  });
178
179  // TODO(benwells): Remove these deprecated versions of the functions.
180  fileSystem.getWritableFileEntry = function() {
181    console.log("chrome.fileSystem.getWritableFileEntry is deprecated");
182    console.log("Please use chrome.fileSystem.getWritableEntry instead");
183    $Function.apply(fileSystem.getWritableEntry, this, arguments);
184  };
185
186  fileSystem.isWritableFileEntry = function() {
187    console.log("chrome.fileSystem.isWritableFileEntry is deprecated");
188    console.log("Please use chrome.fileSystem.isWritableEntry instead");
189    $Function.apply(fileSystem.isWritableEntry, this, arguments);
190  };
191
192  fileSystem.chooseFile = function() {
193    console.log("chrome.fileSystem.chooseFile is deprecated");
194    console.log("Please use chrome.fileSystem.chooseEntry instead");
195    $Function.apply(fileSystem.chooseEntry, this, arguments);
196  };
197});
198
199exports.bindFileEntryCallback = bindFileEntryCallback;
200exports.binding = binding.generate();
201