IsolatedFileSystem.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. /*
  2. * Copyright (C) 2013 Google Inc. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions are
  6. * met:
  7. *
  8. * * Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * * Redistributions in binary form must reproduce the above
  11. * copyright notice, this list of conditions and the following disclaimer
  12. * in the documentation and/or other materials provided with the
  13. * distribution.
  14. * * Neither the name of Google Inc. nor the names of its
  15. * contributors may be used to endorse or promote products derived from
  16. * this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  22. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  24. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  28. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. */
  30. /**
  31. * @constructor
  32. * @param {WebInspector.IsolatedFileSystemManager} manager
  33. * @param {string} path
  34. */
  35. WebInspector.IsolatedFileSystem = function(manager, path, name, rootURL)
  36. {
  37. this._manager = manager;
  38. this._path = path;
  39. this._name = name;
  40. this._rootURL = rootURL;
  41. }
  42. WebInspector.IsolatedFileSystem.errorMessage = function(error)
  43. {
  44. var msg;
  45. switch (error.code) {
  46. case FileError.QUOTA_EXCEEDED_ERR:
  47. msg = "QUOTA_EXCEEDED_ERR";
  48. break;
  49. case FileError.NOT_FOUND_ERR:
  50. msg = "NOT_FOUND_ERR";
  51. break;
  52. case FileError.SECURITY_ERR:
  53. msg = "SECURITY_ERR";
  54. break;
  55. case FileError.INVALID_MODIFICATION_ERR:
  56. msg = "INVALID_MODIFICATION_ERR";
  57. break;
  58. case FileError.INVALID_STATE_ERR:
  59. msg = "INVALID_STATE_ERR";
  60. break;
  61. default:
  62. msg = "Unknown Error";
  63. break;
  64. };
  65. return "File system error: " + msg;
  66. }
  67. WebInspector.IsolatedFileSystem.prototype = {
  68. /**
  69. * @return {string}
  70. */
  71. path: function()
  72. {
  73. return this._path;
  74. },
  75. /**
  76. * @return {string}
  77. */
  78. name: function()
  79. {
  80. return this._name;
  81. },
  82. /**
  83. * @return {string}
  84. */
  85. rootURL: function()
  86. {
  87. return this._rootURL;
  88. },
  89. /**
  90. * @param {function(DOMFileSystem)} callback
  91. */
  92. _requestFileSystem: function(callback)
  93. {
  94. this._manager.requestDOMFileSystem(this._path, callback);
  95. },
  96. /**
  97. * @param {string} path
  98. * @param {function(string)} callback
  99. */
  100. requestFilesRecursive: function(path, callback)
  101. {
  102. this._requestFileSystem(fileSystemLoaded.bind(this));
  103. var domFileSystem;
  104. /**
  105. * @param {DOMFileSystem} fs
  106. */
  107. function fileSystemLoaded(fs)
  108. {
  109. domFileSystem = fs;
  110. this._requestEntries(domFileSystem, path, innerCallback.bind(this));
  111. }
  112. /**
  113. * @param {Array.<FileEntry>} entries
  114. */
  115. function innerCallback(entries)
  116. {
  117. for (var i = 0; i < entries.length; ++i) {
  118. var entry = entries[i];
  119. if (!entry.isDirectory) {
  120. if (this._manager.mapping().isFileExcluded(this._path, entry.fullPath))
  121. continue;
  122. callback(entry.fullPath.substr(1));
  123. }
  124. else {
  125. if (this._manager.mapping().isFileExcluded(this._path, entry.fullPath + "/"))
  126. continue;
  127. this._requestEntries(domFileSystem, entry.fullPath, innerCallback.bind(this));
  128. }
  129. }
  130. }
  131. },
  132. /**
  133. * @param {string} path
  134. * @param {?string} name
  135. * @param {function(?string)} callback
  136. */
  137. createFile: function(path, name, callback)
  138. {
  139. this._requestFileSystem(fileSystemLoaded.bind(this));
  140. var newFileIndex = 1;
  141. if (!name)
  142. name = "NewFile";
  143. var nameCandidate;
  144. /**
  145. * @param {DOMFileSystem} domFileSystem
  146. */
  147. function fileSystemLoaded(domFileSystem)
  148. {
  149. domFileSystem.root.getDirectory(path, null, dirEntryLoaded.bind(this), errorHandler.bind(this));
  150. }
  151. /**
  152. * @param {DirectoryEntry} dirEntry
  153. */
  154. function dirEntryLoaded(dirEntry)
  155. {
  156. var nameCandidate = name;
  157. if (newFileIndex > 1)
  158. nameCandidate += newFileIndex;
  159. ++newFileIndex;
  160. dirEntry.getFile(nameCandidate, { create: true, exclusive: true }, fileCreated, fileCreationError);
  161. function fileCreated(entry)
  162. {
  163. callback(entry.fullPath.substr(1));
  164. }
  165. function fileCreationError(error)
  166. {
  167. if (error.code === FileError.INVALID_MODIFICATION_ERR) {
  168. dirEntryLoaded(dirEntry);
  169. return;
  170. }
  171. var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
  172. console.error(errorMessage + " when testing if file exists '" + (this._path + "/" + path + "/" + nameCandidate) + "'");
  173. callback(null);
  174. }
  175. }
  176. function errorHandler(error)
  177. {
  178. var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
  179. var filePath = this._path + "/" + path;
  180. if (nameCandidate)
  181. filePath += "/" + nameCandidate;
  182. console.error(errorMessage + " when getting content for file '" + (filePath) + "'");
  183. callback(null);
  184. }
  185. },
  186. /**
  187. * @param {string} path
  188. */
  189. deleteFile: function(path)
  190. {
  191. this._requestFileSystem(fileSystemLoaded.bind(this));
  192. /**
  193. * @param {DOMFileSystem} domFileSystem
  194. */
  195. function fileSystemLoaded(domFileSystem)
  196. {
  197. domFileSystem.root.getFile(path, null, fileEntryLoaded.bind(this), errorHandler.bind(this));
  198. }
  199. /**
  200. * @param {FileEntry} fileEntry
  201. */
  202. function fileEntryLoaded(fileEntry)
  203. {
  204. fileEntry.remove(fileEntryRemoved.bind(this), errorHandler.bind(this));
  205. }
  206. function fileEntryRemoved()
  207. {
  208. }
  209. /**
  210. * @param {FileError} error
  211. */
  212. function errorHandler(error)
  213. {
  214. var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
  215. console.error(errorMessage + " when deleting file '" + (this._path + "/" + path) + "'");
  216. }
  217. },
  218. /**
  219. * @param {string} path
  220. * @param {function(?Date, ?number)} callback
  221. */
  222. requestMetadata: function(path, callback)
  223. {
  224. this._requestFileSystem(fileSystemLoaded.bind(this));
  225. /**
  226. * @param {DOMFileSystem} domFileSystem
  227. */
  228. function fileSystemLoaded(domFileSystem)
  229. {
  230. domFileSystem.root.getFile(path, null, fileEntryLoaded, errorHandler);
  231. }
  232. /**
  233. * @param {FileEntry} entry
  234. */
  235. function fileEntryLoaded(entry)
  236. {
  237. entry.getMetadata(successHandler, errorHandler);
  238. }
  239. /**
  240. * @param {Metadata} metadata
  241. */
  242. function successHandler(metadata)
  243. {
  244. callback(metadata.modificationTime, metadata.size);
  245. }
  246. /**
  247. * @param {FileError} error
  248. */
  249. function errorHandler(error)
  250. {
  251. callback(null, null);
  252. }
  253. },
  254. /**
  255. * @param {string} path
  256. * @param {function(?string)} callback
  257. */
  258. requestFileContent: function(path, callback)
  259. {
  260. this._requestFileSystem(fileSystemLoaded.bind(this));
  261. /**
  262. * @param {DOMFileSystem} domFileSystem
  263. */
  264. function fileSystemLoaded(domFileSystem)
  265. {
  266. domFileSystem.root.getFile(path, null, fileEntryLoaded, errorHandler.bind(this));
  267. }
  268. /**
  269. * @param {FileEntry} entry
  270. */
  271. function fileEntryLoaded(entry)
  272. {
  273. entry.file(fileLoaded, errorHandler.bind(this));
  274. }
  275. /**
  276. * @param {!Blob} file
  277. */
  278. function fileLoaded(file)
  279. {
  280. var reader = new FileReader();
  281. reader.onloadend = readerLoadEnd;
  282. reader.readAsText(file);
  283. }
  284. /**
  285. * @this {FileReader}
  286. */
  287. function readerLoadEnd()
  288. {
  289. callback(/** @type {string} */ (this.result));
  290. }
  291. function errorHandler(error)
  292. {
  293. if (error.code === FileError.NOT_FOUND_ERR) {
  294. callback(null);
  295. return;
  296. }
  297. var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
  298. console.error(errorMessage + " when getting content for file '" + (this._path + "/" + path) + "'");
  299. callback(null);
  300. }
  301. },
  302. /**
  303. * @param {string} path
  304. * @param {string} content
  305. * @param {function()} callback
  306. */
  307. setFileContent: function(path, content, callback)
  308. {
  309. this._requestFileSystem(fileSystemLoaded);
  310. /**
  311. * @param {DOMFileSystem} domFileSystem
  312. */
  313. function fileSystemLoaded(domFileSystem)
  314. {
  315. domFileSystem.root.getFile(path, { create: true }, fileEntryLoaded, errorHandler.bind(this));
  316. }
  317. /**
  318. * @param {FileEntry} entry
  319. */
  320. function fileEntryLoaded(entry)
  321. {
  322. entry.createWriter(fileWriterCreated, errorHandler.bind(this));
  323. }
  324. /**
  325. * @param {FileWriter} fileWriter
  326. */
  327. function fileWriterCreated(fileWriter)
  328. {
  329. fileWriter.onerror = errorHandler.bind(this);
  330. fileWriter.onwriteend = fileTruncated;
  331. fileWriter.truncate(0);
  332. function fileTruncated()
  333. {
  334. fileWriter.onwriteend = writerEnd;
  335. var blob = new Blob([content], { type: "text/plain" });
  336. fileWriter.write(blob);
  337. }
  338. }
  339. function writerEnd()
  340. {
  341. callback();
  342. }
  343. function errorHandler(error)
  344. {
  345. var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
  346. console.error(errorMessage + " when setting content for file '" + (this._path + "/" + path) + "'");
  347. callback();
  348. }
  349. },
  350. /**
  351. * @param {string} path
  352. * @param {string} newName
  353. * @param {function(boolean, string=)} callback
  354. */
  355. renameFile: function(path, newName, callback)
  356. {
  357. newName = newName ? newName.trim() : newName;
  358. if (!newName || newName.indexOf("/") !== -1) {
  359. callback(false);
  360. return;
  361. }
  362. var fileEntry;
  363. var dirEntry;
  364. var newFileEntry;
  365. this._requestFileSystem(fileSystemLoaded);
  366. /**
  367. * @param {DOMFileSystem} domFileSystem
  368. */
  369. function fileSystemLoaded(domFileSystem)
  370. {
  371. domFileSystem.root.getFile(path, null, fileEntryLoaded, errorHandler.bind(this));
  372. }
  373. /**
  374. * @param {FileEntry} entry
  375. */
  376. function fileEntryLoaded(entry)
  377. {
  378. if (entry.name === newName) {
  379. callback(false);
  380. return;
  381. }
  382. fileEntry = entry;
  383. fileEntry.getParent(dirEntryLoaded, errorHandler.bind(this));
  384. }
  385. /**
  386. * @param {Entry} entry
  387. */
  388. function dirEntryLoaded(entry)
  389. {
  390. dirEntry = entry;
  391. dirEntry.getFile(newName, null, newFileEntryLoaded, newFileEntryLoadErrorHandler);
  392. }
  393. /**
  394. * @param {FileEntry} entry
  395. */
  396. function newFileEntryLoaded(entry)
  397. {
  398. callback(false);
  399. }
  400. function newFileEntryLoadErrorHandler(error)
  401. {
  402. if (error.code !== FileError.NOT_FOUND_ERR) {
  403. callback(false);
  404. return;
  405. }
  406. fileEntry.moveTo(dirEntry, newName, fileRenamed, errorHandler.bind(this));
  407. }
  408. /**
  409. * @param {FileEntry} entry
  410. */
  411. function fileRenamed(entry)
  412. {
  413. callback(true, entry.name);
  414. }
  415. function errorHandler(error)
  416. {
  417. var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
  418. console.error(errorMessage + " when renaming file '" + (this._path + "/" + path) + "' to '" + newName + "'");
  419. callback(false);
  420. }
  421. },
  422. /**
  423. * @param {DirectoryEntry} dirEntry
  424. * @param {function(Array.<FileEntry>)} callback
  425. */
  426. _readDirectory: function(dirEntry, callback)
  427. {
  428. var dirReader = dirEntry.createReader();
  429. var entries = [];
  430. function innerCallback(results)
  431. {
  432. if (!results.length)
  433. callback(entries.sort());
  434. else {
  435. entries = entries.concat(toArray(results));
  436. dirReader.readEntries(innerCallback, errorHandler);
  437. }
  438. }
  439. function toArray(list)
  440. {
  441. return Array.prototype.slice.call(list || [], 0);
  442. }
  443. dirReader.readEntries(innerCallback, errorHandler);
  444. function errorHandler(error)
  445. {
  446. var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
  447. console.error(errorMessage + " when reading directory '" + dirEntry.fullPath + "'");
  448. callback([]);
  449. }
  450. },
  451. /**
  452. * @param {DOMFileSystem} domFileSystem
  453. * @param {string} path
  454. * @param {function(Array.<FileEntry>)} callback
  455. */
  456. _requestEntries: function(domFileSystem, path, callback)
  457. {
  458. domFileSystem.root.getDirectory(path, null, innerCallback.bind(this), errorHandler);
  459. function innerCallback(dirEntry)
  460. {
  461. this._readDirectory(dirEntry, callback)
  462. }
  463. function errorHandler(error)
  464. {
  465. var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
  466. console.error(errorMessage + " when requesting entry '" + path + "'");
  467. callback([]);
  468. }
  469. }
  470. }