- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / renderer / resources / extensions / file_system_custom_bindings.js
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
7 var binding = require('binding').Binding.create('fileSystem');
8
9 var fileSystemNatives = requireNative('file_system_natives');
10 var GetIsolatedFileSystem = fileSystemNatives.GetIsolatedFileSystem;
11 var lastError = require('lastError');
12 var sendRequest = require('sendRequest').sendRequest;
13 var GetModuleSystem = requireNative('v8_context').GetModuleSystem;
14 // TODO(sammc): Don't require extension. See http://crbug.com/235689.
15 var GetExtensionViews = requireNative('extension').GetExtensionViews;
16
17 // Fallback to using the current window if no background page is running.
18 var backgroundPage = GetExtensionViews(-1, 'BACKGROUND')[0] || window;
19 var 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.
25 if (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                   callback(entries);
71                 } else {
72                   callback(entries[0]);
73                 }
74               }
75             }
76             // TODO(koz): fs.root.getFile() makes a trip to the browser process,
77             // but it might be possible avoid that by calling
78             // WebFrame::createFileEntry().
79             if (entry.isDirectory) {
80               fs.root.getDirectory(baseName, {}, getEntryCallback,
81                                    getEntryError);
82             } else {
83               fs.root.getFile(baseName, {}, getEntryCallback, getEntryError);
84             }
85           } catch (e) {
86             if (!hasError) {
87               hasError = true;
88               lastError.run('fileSystem.' + functionName,
89                             'Error getting fileEntry: ' + e.stack,
90                             request.stack,
91                             callback);
92             }
93           }
94         });
95       }
96     });
97   };
98   var entryIdManager = require('entryIdManager');
99 } else {
100   // Force the fileSystem API to be loaded in the background page. Using
101   // backgroundPageModuleSystem.require('fileSystem') is insufficient as
102   // requireNative is only allowed while lazily loading an API.
103   backgroundPage.chrome.fileSystem;
104   var bindFileEntryCallback = backgroundPageModuleSystem.require(
105       'fileSystem').bindFileEntryCallback;
106   var entryIdManager = backgroundPageModuleSystem.require('entryIdManager');
107 }
108
109 binding.registerCustomHook(function(bindingsAPI) {
110   var apiFunctions = bindingsAPI.apiFunctions;
111   var fileSystem = bindingsAPI.compiledApi;
112
113   function bindFileEntryFunction(functionName) {
114     apiFunctions.setUpdateArgumentsPostValidate(
115         functionName, function(fileEntry, callback) {
116       var fileSystemName = fileEntry.filesystem.name;
117       var relativePath = $String.slice(fileEntry.fullPath, 1);
118       return [fileSystemName, relativePath, callback];
119     });
120   }
121   $Array.forEach(['getDisplayPath', 'getWritableEntry', 'isWritableEntry'],
122                   bindFileEntryFunction);
123
124   $Array.forEach(['getWritableEntry', 'chooseEntry', 'restoreEntry'],
125                   function(functionName) {
126     bindFileEntryCallback(functionName, apiFunctions);
127   });
128
129   apiFunctions.setHandleRequest('retainEntry', function(fileEntry) {
130     var id = entryIdManager.getEntryId(fileEntry);
131     if (!id)
132       return '';
133     var fileSystemName = fileEntry.filesystem.name;
134     var relativePath = $String.slice(fileEntry.fullPath, 1);
135
136     sendRequest(this.name, [id, fileSystemName, relativePath],
137       this.definition.parameters, {});
138     return id;
139   });
140
141   apiFunctions.setHandleRequest('isRestorable',
142       function(id, callback) {
143     var savedEntry = entryIdManager.getEntryById(id);
144     if (savedEntry) {
145       callback(true);
146     } else {
147       sendRequest(this.name, [id, callback], this.definition.parameters, {});
148     }
149   });
150
151   apiFunctions.setUpdateArgumentsPostValidate('restoreEntry',
152       function(id, callback) {
153     var savedEntry = entryIdManager.getEntryById(id);
154     if (savedEntry) {
155       // We already have a file entry for this id so pass it to the callback and
156       // send a request to the browser to move it to the back of the LRU.
157       callback(savedEntry);
158       return [id, false, null];
159     } else {
160       // Ask the browser process for a new file entry for this id, to be passed
161       // to |callback|.
162       return [id, true, callback];
163     }
164   });
165
166   // TODO(benwells): Remove these deprecated versions of the functions.
167   fileSystem.getWritableFileEntry = function() {
168     console.log("chrome.fileSystem.getWritableFileEntry is deprecated");
169     console.log("Please use chrome.fileSystem.getWritableEntry instead");
170     $Function.apply(fileSystem.getWritableEntry, this, arguments);
171   };
172
173   fileSystem.isWritableFileEntry = function() {
174     console.log("chrome.fileSystem.isWritableFileEntry is deprecated");
175     console.log("Please use chrome.fileSystem.isWritableEntry instead");
176     $Function.apply(fileSystem.isWritableEntry, this, arguments);
177   };
178
179   fileSystem.chooseFile = function() {
180     console.log("chrome.fileSystem.chooseFile is deprecated");
181     console.log("Please use chrome.fileSystem.chooseEntry instead");
182     $Function.apply(fileSystem.chooseEntry, this, arguments);
183   };
184 });
185
186 exports.bindFileEntryCallback = bindFileEntryCallback;
187 exports.binding = binding.generate();