author | Barret Rennie <barret@brennie.ca> |
Fri, 12 May 2023 18:34:28 +0000 (2023-05-12) | |
changeset 663653 | c698636dbfea4e9d7a50338981b7a4862404ea92 |
parent 663652 | 54c0ab259e184fc9a88fa6fe00c8092f1070dafa |
child 663654 | 973b6e904e8ce2b8b90ef0caba78fb8d45c22ad2 |
push id | 40851 |
push user | nerli@mozilla.com |
push date | Sat, 13 May 2023 09:21:59 +0000 (2023-05-13) |
treeherder | mozilla-central@d36ba840f6ab [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | Gijs, webidl, smaug |
bugs | 1776480, 1786885, 1775167, 1830097, 1830100 |
milestone | 115.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/.eslintrc-test-paths.js +++ b/.eslintrc-test-paths.js @@ -258,17 +258,16 @@ const extraChromeTestPaths = [ "gfx/layers/apz/test/mochitest/", "image/test/mochitest/", "layout/forms/test/", "layout/generic/test/", "layout/mathml/tests/", "layout/svg/tests/", "layout/xul/test/", "toolkit/components/aboutmemory/tests/", - "toolkit/components/osfile/tests/mochi/", "toolkit/components/printing/tests/", "toolkit/components/url-classifier/tests/mochitest/", "toolkit/components/viewsource/test/", "toolkit/components/windowcreator/test/", "toolkit/components/windowwatcher/test/", "toolkit/components/workerloader/tests/", "toolkit/content/tests/widgets/", "toolkit/profile/test/",
--- a/.eslintrc.js +++ b/.eslintrc.js @@ -305,17 +305,16 @@ module.exports = { "toolkit/components/glean/tests/browser/**", "toolkit/components/kvstore/kvstore.sys.mjs", "toolkit/components/lz4/lz4.js", "toolkit/components/messaging-system/**", "toolkit/components/mozintl/mozIntl.sys.mjs", "toolkit/components/narrate/Narrator.jsm", "toolkit/components/nimbus/**", "toolkit/components/normandy/**", - "toolkit/components/osfile/**", "toolkit/components/passwordmgr/**", "toolkit/components/pdfjs/**", "toolkit/components/pictureinpicture/**", "toolkit/components/places/**", "toolkit/components/printing/content/print*.*", "toolkit/components/processtools/tests/browser/browser_test_powerMetrics.js", "toolkit/components/promiseworker/**/PromiseWorker.*", "toolkit/components/prompts/**", @@ -609,23 +608,16 @@ module.exports = { extends: ["plugin:react-hooks/recommended"], files: [ "browser/components/newtab/**", "browser/components/pocket/**", "devtools/**", ], }, { - // Turn off the osfile rule for osfile. - files: ["toolkit/components/osfile/**"], - rules: { - "mozilla/reject-osfile": "off", - }, - }, - { // Exempt files with these paths since they have to use http for full coverage files: httpTestingPaths.map(path => `${path}**`), rules: { "@microsoft/sdl/no-insecure-url": "off", }, }, { // Exempt all components and test files that explicitly want to test http urls from 'no-insecure-url' rule.
--- a/browser/base/content/test/performance/browser_startup.js +++ b/browser/base/content/test/performance/browser_startup.js @@ -97,17 +97,16 @@ const startupPhases = { // Things that are expected to be completely out of the startup path // and loaded lazily when used for the first time by the user should // be listed here. "before becoming idle": { denylist: { modules: new Set([ "resource://gre/modules/AsyncPrefs.sys.mjs", "resource://gre/modules/LoginManagerContextMenu.sys.mjs", - "resource://gre/modules/osfile.jsm", "resource://pdf.js/PdfStreamConverter.sys.mjs", ]), }, }, }; if ( Services.prefs.getBoolPref("browser.startup.blankWindow") &&
--- a/browser/base/content/test/static/browser_all_files_referenced.js +++ b/browser/base/content/test/static/browser_all_files_referenced.js @@ -66,22 +66,16 @@ var gExceptionPaths = [ // Nimbus schemas are referenced programmatically. "resource://nimbus/schemas/", // Activity stream schemas are referenced programmatically. "resource://activity-stream/schemas", // Localization file added programatically in featureCallout.jsm "resource://app/localization/en-US/browser/featureCallout.ftl", - - // Will be removed in bug 1737308 - "resource://gre/modules/lz4.js", - "resource://gre/modules/lz4_internal.js", - "resource://gre/modules/osfile.jsm", - "resource://gre/modules/osfile/", ]; // These are not part of the omni.ja file, so we find them only when running // the test on a non-packaged build. if (AppConstants.platform == "macosx") { gExceptionPaths.push("resource://gre/res/cursors/"); gExceptionPaths.push("resource://gre/res/touchbar/"); }
--- a/docs/code-quality/lint/linters/eslint-plugin-mozilla.rst +++ b/docs/code-quality/lint/linters/eslint-plugin-mozilla.rst @@ -53,17 +53,16 @@ The plugin implements the following rule eslint-plugin-mozilla/reject-chromeutils-import-params eslint-plugin-mozilla/reject-eager-module-in-lazy-getter eslint-plugin-mozilla/reject-global-this eslint-plugin-mozilla/reject-globalThis-modification eslint-plugin-mozilla/reject-importGlobalProperties eslint-plugin-mozilla/reject-lazy-imports-into-globals eslint-plugin-mozilla/reject-mixing-eager-and-lazy eslint-plugin-mozilla/reject-multiple-getters-calls - eslint-plugin-mozilla/reject-osfile eslint-plugin-mozilla/reject-relative-requires eslint-plugin-mozilla/reject-requires-await eslint-plugin-mozilla/reject-scriptableunicodeconverter eslint-plugin-mozilla/reject-some-requires eslint-plugin-mozilla/reject-top-level-await eslint-plugin-mozilla/reject-import-system-module-from-non-system eslint-plugin-mozilla/use-cc-etc eslint-plugin-mozilla/use-chromeutils-generateqi
deleted file mode 100644 --- a/docs/code-quality/lint/linters/eslint-plugin-mozilla/reject-osfile.rst +++ /dev/null @@ -1,13 +0,0 @@ -reject-osfile -============= - -Rejects calls into ``OS.File`` and ``OS.Path``. This is configured as a warning. -You should use |IOUtils|_ and |PathUtils|_ respectively for new code. If -modifying old code, please consider swapping it in if possible; if this is -tricky please ensure a bug is on file. - -.. |IOUtils| replace:: ``IOUtils`` -.. _IOUtils: https://searchfox.org/mozilla-central/source/dom/chrome-webidl/IOUtils.webidl - -.. |PathUtils| replace:: ``PathUtils`` -.. _PathUtils: https://searchfox.org/mozilla-central/source/dom/chrome-webidl/PathUtils.webidl
--- a/dom/system/OSFileConstants.cpp +++ b/dom/system/OSFileConstants.cpp @@ -830,107 +830,22 @@ bool OSFileConstantsService::DefineOSFil // Build OS.Constants.Sys JS::Rooted<JSObject*> objSys(aCx); if (!(objSys = GetOrCreateObjectProperty(aCx, objConstants, "Sys"))) { return false; } - nsCOMPtr<nsIXULRuntime> runtime = - do_GetService(XULRUNTIME_SERVICE_CONTRACTID); - if (runtime) { - nsAutoCString os; - DebugOnly<nsresult> rv = runtime->GetOS(os); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - - JSString* strVersion = JS_NewStringCopyZ(aCx, os.get()); - if (!strVersion) { - return false; - } - - JS::Rooted<JS::Value> valVersion(aCx, JS::StringValue(strVersion)); - if (!JS_SetProperty(aCx, objSys, "Name", valVersion)) { - return false; - } - } - -#if defined(DEBUG) - JS::Rooted<JS::Value> valDebug(aCx, JS::TrueValue()); - if (!JS_SetProperty(aCx, objSys, "DEBUG", valDebug)) { - return false; - } -#endif - -#if defined(HAVE_64BIT_BUILD) - JS::Rooted<JS::Value> valBits(aCx, JS::Int32Value(64)); -#else - JS::Rooted<JS::Value> valBits(aCx, JS::Int32Value(32)); -#endif // defined (HAVE_64BIT_BUILD) - if (!JS_SetProperty(aCx, objSys, "bits", valBits)) { - return false; - } - if (!JS_DefineProperty( aCx, objSys, "umask", mUserUmask, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) { return false; } - // Build OS.Constants.Path - - JS::Rooted<JSObject*> objPath(aCx); - if (!(objPath = GetOrCreateObjectProperty(aCx, objConstants, "Path"))) { - return false; - } - - // Locate libxul - // Note that we don't actually provide the full path, only the name of the - // library, which is sufficient to link to the library using js-ctypes. - -#if defined(XP_MACOSX) - // Under MacOS X, for some reason, libxul is called simply "XUL", - // and we need to provide the full path. - nsAutoString libxul; - libxul.Append(mPaths->libDir); - libxul.AppendLiteral("/XUL"); -#else - // On other platforms, libxul is a library "xul" with regular - // library prefix/suffix. - nsAutoString libxul; - libxul.AppendLiteral(MOZ_DLL_PREFIX); - libxul.AppendLiteral("xul"); - libxul.AppendLiteral(MOZ_DLL_SUFFIX); -#endif // defined(XP_MACOSX) - - if (!SetStringProperty(aCx, objPath, "libxul", libxul)) { - return false; - } - - if (!SetStringProperty(aCx, objPath, "libDir", mPaths->libDir)) { - return false; - } - - if (!SetStringProperty(aCx, objPath, "tmpDir", mPaths->tmpDir)) { - return false; - } - - // Configure profileDir only if it is available at this stage - if (!mPaths->profileDir.IsVoid() && - !SetStringProperty(aCx, objPath, "profileDir", mPaths->profileDir)) { - return false; - } - - // Configure localProfileDir only if it is available at this stage - if (!mPaths->localProfileDir.IsVoid() && - !SetStringProperty(aCx, objPath, "localProfileDir", - mPaths->localProfileDir)) { - return false; - } - return true; } NS_IMPL_ISUPPORTS(OSFileConstantsService, nsIOSFileConstantsService, nsIObserver) /* static */ already_AddRefed<OSFileConstantsService> OSFileConstantsService::GetOrCreate() {
--- a/dom/system/moz.build +++ b/dom/system/moz.build @@ -3,19 +3,16 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # This picks up *hapticfeedback* which is graveyard with Files("**"): BUG_COMPONENT = ("Core", "DOM: Core & HTML") -with Files("*OSFile*"): - BUG_COMPONENT = ("Toolkit", "OS.File") - with Files("*ocationProvider*"): BUG_COMPONENT = ("Core", "DOM: Geolocation") with Files("windows/*LocationProvider*"): BUG_COMPONENT = ("Core", "DOM: Geolocation") with Files("IOUtils*"): BUG_COMPONENT = ("Toolkit", "OS.File")
--- a/dom/system/tests/chrome.ini +++ b/dom/system/tests/chrome.ini @@ -1,9 +1,9 @@ [DEFAULT] + +[test_constants.xhtml] support-files = worker_constants.js - -[test_constants.xhtml] [test_pathutils.html] [test_pathutils_worker.xhtml] support-files = pathutils_worker.js
--- a/dom/system/tests/ioutils/test_ioutils_read_write.html +++ b/dom/system/tests/ioutils/test_ioutils_read_write.html @@ -10,21 +10,16 @@ <link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> <script src="file_ioutils_test_fixtures.js"></script> <script> "use strict"; const { Assert } = ChromeUtils.import("resource://testing-common/Assert.jsm"); const { ObjectUtils } = ChromeUtils.import("resource://gre/modules/ObjectUtils.jsm"); - // This is presently only used to test compatability between OS.File and - // IOUtils when it comes to writing compressed files. The import and the - // test `test_lz4_osfile_compat` can be removed with OS.File is removed. - const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - add_task(async function test_read_failure() { const doesNotExist = PathUtils.join(PathUtils.tempDir, "does_not_exist.tmp"); await Assert.rejects( IOUtils.read(doesNotExist), /Could not open the file at .*/, "IOUtils::read rejects when file does not exist" ); }); @@ -335,41 +330,16 @@ const readEmptyRaw = await IOUtils.read(tmpFileName, { decompress: false }); is(readEmptyRaw.length, 12, "Expected to read back just the LZ4 header"); const expectedHeader = Uint8Array.of(109, 111, 122, 76, 122, 52, 48, 0, 0, 0, 0, 0); // "mozLz40\0\0\0\0" ok(readEmptyRaw.equals(expectedHeader), "Expected to read header with content length of 0"); await cleanup(tmpFileName); }); - add_task(async function test_lz4_osfile_compat() { - const osfileTmpFile = PathUtils.join(PathUtils.tempDir, "test_ioutils_lz4_compat_osfile.tmp"); - const ioutilsTmpFile = PathUtils.join(PathUtils.tempDir, "test_ioutils_lz4_compat_ioutils.tmp"); - - info("Test OS.File and IOUtils write the same file with LZ4 compression enabled") - const repeatedBytes = Uint8Array.of(...new Array(50).fill(1)); - let expectedBytes = 23; - let ioutilsBytes = await IOUtils.write(ioutilsTmpFile, repeatedBytes, { compress: true }); - let osfileBytes = await OS.File.writeAtomic(osfileTmpFile, repeatedBytes, { compression: "lz4" }); - is(ioutilsBytes, expectedBytes, "IOUtils writes the expected number of bytes for compression"); - is(osfileBytes, ioutilsBytes, "OS.File and IOUtils write the same number of bytes for LZ4 compression"); - - info("Test OS.File can read a file compressed by IOUtils"); - const osfileReadBytes = await OS.File.read(ioutilsTmpFile, { compression: "lz4" }); - ok(osfileReadBytes.every(byte => byte === 1), "OS.File can read a file compressed by IOUtils"); - is(osfileReadBytes.length, 50, "OS.File reads the right number of bytes from a file compressed by IOUtils") - - info("Test IOUtils can read a file compressed by OS.File"); - const ioutilsReadBytes = await IOUtils.read(osfileTmpFile, { decompress: true }); - ok(ioutilsReadBytes.every(byte => byte === 1), "IOUtils can read a file compressed by OS.File"); - is(ioutilsReadBytes.length, 50, "IOUtils reads the right number of bytes from a file compressed by OS.File") - - await cleanup(osfileTmpFile, ioutilsTmpFile); - }); - add_task(async function test_lz4_bad_call() { const tmpFileName = PathUtils.join(PathUtils.tempDir, "test_ioutils_lz4_bad_call.tmp"); info("Test decompression with invalid options"); const varyingBytes = Uint8Array.of(...new Array(50).keys()); let bytesWritten = await IOUtils.write(tmpFileName, varyingBytes, { compress: true }); is(bytesWritten, 64, "Expected to write 64 bytes"); await Assert.rejects(
--- a/dom/system/tests/ioutils/test_ioutils_read_write_utf8.html +++ b/dom/system/tests/ioutils/test_ioutils_read_write_utf8.html @@ -10,21 +10,16 @@ <link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> <script src="file_ioutils_test_fixtures.js"></script> <script> "use strict"; const { Assert } = ChromeUtils.import("resource://testing-common/Assert.jsm"); const { ObjectUtils } = ChromeUtils.import("resource://gre/modules/ObjectUtils.jsm"); - // This is presently only used to test compatability between OS.File and - // IOUtils when it comes to writing compressed files. The import and the - // test `test_lz4_osfile_compat` can be removed with OS.File is removed. - const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - // This is an impossible sequence of bytes in an UTF-8 encoded file. // See section 3.5.3 of this text: // https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt const invalidUTF8 = Uint8Array.of(0xfe, 0xfe, 0xff, 0xff); add_task(async function test_read_utf8_failure() { info("Test attempt to read non-existent file (UTF8)"); const doesNotExist = PathUtils.join(PathUtils.tempDir, "does_not_exist.tmp"); @@ -285,39 +280,16 @@ const readEmpty = await IOUtils.readUTF8(tmpFileName, { decompress: true }); is(readEmpty, empty, "IOUtils can write and read back empty buffers with LZ4"); const readEmptyRaw = await IOUtils.readUTF8(tmpFileName, { decompress: false }); is(readEmptyRaw.length, 12, "Expected to read back just the LZ4 header"); await cleanup(tmpFileName); }); - add_task(async function test_utf8_lz4_osfile_compat() { - const osfileTmpFile = PathUtils.join(PathUtils.tempDir, "test_ioutils_utf8_lz4_compat_osfile.tmp"); - const ioutilsTmpFile = PathUtils.join(PathUtils.tempDir, "test_ioutils_utf8_lz4_compat_ioutils.tmp"); - - info("Test OS.File and IOUtils write the same UTF-8 file with LZ4 compression enabled") - const emoji = "☕️ ⚧️ 😀 🖖🏿 🤠 🏳️🌈 🥠 🏴☠️ 🪐"; - let expectedBytes = 83; - let ioutilsBytes = await IOUtils.writeUTF8(ioutilsTmpFile, emoji, { compress: true }); - let osfileBytes = await OS.File.writeAtomic(osfileTmpFile, emoji, { compression: "lz4" }); - is(ioutilsBytes, expectedBytes, "IOUtils writes the expected number of bytes for compression"); - is(osfileBytes, ioutilsBytes, "OS.File and IOUtils write the same number of bytes for LZ4 compression"); - - info("Test OS.File can read an UTF-8 file compressed by IOUtils"); - const osfileReadStr = await OS.File.read(ioutilsTmpFile, { compression: "lz4", encoding: "utf-8" }); - is(osfileReadStr, emoji, "OS.File can read an UTF-8 file compressed by IOUtils") - - info("Test IOUtils can read an UTF-8 file compressed by OS.File"); - const ioutilsReadString = await IOUtils.readUTF8(ioutilsTmpFile, { decompress: true }); - is(ioutilsReadString, emoji, "IOUtils can read an UTF-8 file compressed by OS.File"); - - await cleanup(osfileTmpFile, ioutilsTmpFile); - }); - add_task(async function test_utf8_lz4_bad_call() { const tmpFileName = PathUtils.join(PathUtils.tempDir, "test_ioutils_utf8_lz4_bad_call.tmp"); info("readUTF8 ignores the maxBytes option if provided"); const emoji = "☕️ ⚧️ 😀 🖖🏿 🤠 🏳️🌈 🥠 🏴☠️ 🪐"; let bytesWritten = await IOUtils.writeUTF8(tmpFileName, emoji, { compress: true }); is(bytesWritten, 83, "Expected to write 83 bytes");
--- a/dom/system/tests/test_constants.xhtml +++ b/dom/system/tests/test_constants.xhtml @@ -11,31 +11,16 @@ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/> <script type="application/javascript"> <![CDATA[ /* global OS */ let worker; -function test_xul() { - let lib; - isnot(null, OS.Constants.Path.libxul, "libxulpath is defined"); - try { - lib = ctypes.open(OS.Constants.Path.libxul); - lib.declare("DumpJSStack", ctypes.default_abi, ctypes.void_t); - } catch (x) { - ok(false, "Could not open libxul " + x); - } - if (lib) { - lib.close(); - } - ok(true, "test_xul: opened libxul successfully"); -} - // Test that OS.Constants.libc is defined function test_libc() { isnot(null, OS.Constants.libc, "OS.Constants.libc is defined"); is(0o001, OS.Constants.libc.S_IXOTH, "OS.Constants.libc.S_IXOTH is defined"); is(0o002, OS.Constants.libc.S_IWOTH, "OS.Constants.libc.S_IWOTH is defined"); is(0o007, OS.Constants.libc.S_IRWXO, "OS.Constants.libc.S_IRWXO is defined"); is(0o010, OS.Constants.libc.S_IXGRP, "OS.Constants.libc.S_IXGRP is defined"); is(0o020, OS.Constants.libc.S_IWGRP, "OS.Constants.libc.S_IWGRP is defined"); @@ -53,44 +38,35 @@ function test_Win() { .getService(Ci.nsIXULRuntime); if(xulRuntime.OS == "Windows") { ok("Win" in OS.Constants, "OS.Constants.Win is defined"); is(OS.Constants.Win.INVALID_HANDLE_VALUE, -1, "OS.Constants.Win.INVALID_HANDLE_VALUE is defined and correct"); } } -// Test that OS.Constants.Sys.DEBUG is set properly on main thread -function test_debugBuildMainThread(isDebugBuild) { - is(isDebugBuild, !!OS.Constants.Sys.DEBUG, "OS.Constants.Sys.DEBUG is set properly on main thread"); -} - // Test that OS.Constants.Sys.umask is set properly on main thread function test_umaskMainThread(umask) { is(umask, OS.Constants.Sys.umask, "OS.Constants.Sys.umask is set properly on main thread: " + ("0000"+umask.toString(8)).slice(-4)); } var ctypes; function test() { ok(true, "test_constants.xhtml: Starting test"); // Test 1: Load libxul from main thread Cc["@mozilla.org/net/osfileconstantsservice;1"]. getService(Ci.nsIOSFileConstantsService). init(); ({ctypes} = ChromeUtils.import("resource://gre/modules/ctypes.jsm")); - test_xul(); test_libc(); test_Win(); - let isDebugBuild = Cc["@mozilla.org/xpcom/debug;1"] - .getService(Ci.nsIDebug2).isDebugBuild; - test_debugBuildMainThread(isDebugBuild); let umask = Cc["@mozilla.org/system-info;1"]. getService(Ci.nsIPropertyBag2). getProperty("umask"); test_umaskMainThread(umask); // Test 2: Load libxul from chrome thread worker = new ChromeWorker("worker_constants.js"); @@ -118,17 +94,16 @@ function test() { SimpleTest.ok(false, "test_constants.xhtml: wrong message " + JSON.stringify(msg.data)); return; } }; // pass expected values that are unavailable off-main-thread // to the worker worker.postMessage({ - isDebugBuild: isDebugBuild, umask: umask }); ok(true, "test_constants.xhtml: Test in progress"); }; ]]> </script> <body xmlns="http://www.w3.org/1999/xhtml">
--- a/dom/system/tests/worker_constants.js +++ b/dom/system/tests/worker_constants.js @@ -10,23 +10,19 @@ function log(text) { function send(message) { self.postMessage(message); } self.onmessage = function(msg) { self.onmessage = function(msgInner) { log("ignored message " + JSON.stringify(msgInner.data)); }; - let { isDebugBuild, umask } = msg.data; + let { umask } = msg.data; try { - test_name(); - test_xul(); - test_debugBuildWorkerThread(isDebugBuild); test_umaskWorkerThread(umask); - test_bits(); } catch (x) { log("Catching error: " + x); log("Stack: " + x.stack); log("Source: " + x.toSource()); ok(false, x.toString() + "\n" + x.stack); } finish(); }; @@ -40,56 +36,17 @@ function ok(condition, description) { } function is(a, b, description) { send({ kind: "is", a, b, description }); } function isnot(a, b, description) { send({ kind: "isnot", a, b, description }); } -// Test that OS.Constants.Sys.Name is defined -function test_name() { - isnot(null, OS.Constants.Sys.Name, "OS.Constants.Sys.Name is defined"); -} - -// Test that OS.Constants.Sys.DEBUG is set properly in ChromeWorker thread -function test_debugBuildWorkerThread(isDebugBuild) { - is( - isDebugBuild, - !!OS.Constants.Sys.DEBUG, - "OS.Constants.Sys.DEBUG is set properly on worker thread" - ); -} - // Test that OS.Constants.Sys.umask is set properly in ChromeWorker thread function test_umaskWorkerThread(umask) { is( umask, OS.Constants.Sys.umask, "OS.Constants.Sys.umask is set properly on worker thread: " + ("0000" + umask.toString(8)).slice(-4) ); } - -// Test that OS.Constants.Path.libxul lets us open libxul -function test_xul() { - let lib; - isnot(null, OS.Constants.Path.libxul, "libxul is defined"); - try { - lib = ctypes.open(OS.Constants.Path.libxul); - lib.declare("DumpJSStack", ctypes.default_abi, ctypes.void_t); - } catch (x) { - ok(false, "test_xul: Could not open libxul: " + x); - } - if (lib) { - lib.close(); - } - ok(true, "test_xul: opened libxul successfully"); -} - -// Check if the value of OS.Constants.Sys.bits is 32 or 64 -function test_bits() { - is( - OS.Constants.Sys.bits, - ctypes.int.ptr.size * 8, - "OS.Constants.Sys.bits is either 32 or 64" - ); -}
deleted file mode 100644 --- a/dom/webidl/NativeOSFileInternals.webidl +++ /dev/null @@ -1,60 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtaone at http://mozilla.org/MPL/2.0/. */ - -/** - * Options for nsINativeOSFileInternals::Read - */ -[GenerateInit] -dictionary NativeOSFileReadOptions -{ - /** - * If specified, convert the raw bytes to a String - * with the specified encoding. Otherwise, return - * the raw bytes as a TypedArray. - */ - DOMString? encoding; - - /** - * If specified, limit the number of bytes to read. - */ - unsigned long long? bytes; -}; - -/** - * Options for nsINativeOSFileInternals::WriteAtomic - */ -[GenerateInit] -dictionary NativeOSFileWriteAtomicOptions -{ - /** - * If specified, specify the number of bytes to write. - * NOTE: This takes (and should take) a uint64 here but the actual - * value is limited to int32. This needs to be fixed, see Bug 1063635. - */ - unsigned long long? bytes; - - /** - * If specified, write all data to a temporary file in the - * |tmpPath|. Else, write to the given path directly. - */ - DOMString? tmpPath = null; - - /** - * If specified and true, a failure will occur if the file - * already exists in the given path. - */ - boolean noOverwrite = false; - - /** - * If specified and true, this will sync any buffered data - * for the file to disk. This might be slower, but safer. - */ - boolean flush = false; - - /** - * If specified, this will backup the destination file as - * specified. - */ - DOMString? backupTo = null; -};
--- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -224,19 +224,16 @@ with Files("MIDI*"): BUG_COMPONENT = ("Core", "DOM: Device Interfaces") with Files("Mouse*"): BUG_COMPONENT = ("Core", "DOM: UI Events & Focus Handling") with Files("MutationEvent.webidl"): BUG_COMPONENT = ("Core", "DOM: Events") -with Files("NativeOSFileInternals.webidl"): - BUG_COMPONENT = ("Toolkit", "OS.File") - with Files("NavigationPreloadManager.webidl"): BUG_COMPONENT = ("Core", "DOM: Service Workers") with Files("Net*"): BUG_COMPONENT = ("Core", "Networking") with Files("OfflineAudio*"): BUG_COMPONENT = ("Core", "Web Audio") @@ -741,17 +738,16 @@ WEBIDL_FILES = [ "MimeType.webidl", "MimeTypeArray.webidl", "MouseEvent.webidl", "MouseScrollEvent.webidl", "MozFrameLoaderOwner.webidl", "MutationEvent.webidl", "MutationObserver.webidl", "NamedNodeMap.webidl", - "NativeOSFileInternals.webidl", "NavigationPreloadManager.webidl", "Navigator.webidl", "NetErrorInfo.webidl", "NetworkInformation.webidl", "NetworkOptions.webidl", "NodeFilter.webidl", "NodeIterator.webidl", "NodeList.webidl",
--- a/js/xpconnect/tests/browser/browser_import_mapped_jsm.js +++ b/js/xpconnect/tests/browser/browser_import_mapped_jsm.js @@ -23,17 +23,16 @@ const JSMs = [ "resource://gre/modules/FileUtils.jsm", "resource://gre/modules/LightweightThemeManager.jsm", "resource://gre/modules/NetUtil.jsm", "resource://gre/modules/PlacesUtils.jsm", "resource://gre/modules/PluralForm.jsm", "resource://gre/modules/PrivateBrowsingUtils.jsm", "resource://gre/modules/Timer.jsm", "resource://gre/modules/XPCOMUtils.jsm", - "resource://gre/modules/osfile.jsm", "resource://gre/modules/addons/XPIDatabase.jsm", "resource://gre/modules/addons/XPIProvider.jsm", "resource://gre/modules/addons/XPIInstall.jsm", "resource:///modules/BrowserWindowTracker.jsm", ]; if (AppConstants.platform === "win") { JSMs.push("resource:///modules/WindowsJumpLists.jsm");
--- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -619,19 +619,16 @@ pref("findbar.matchdiacritics", 0); pref("findbar.modalHighlight", false); // use Mac OS X Appearance panel text smoothing setting when rendering text, disabled by default pref("gfx.use_text_smoothing_setting", false); // Number of characters to consider emphasizing for rich autocomplete results pref("toolkit.autocomplete.richBoundaryCutoff", 200); -// Variable controlling logging for osfile. -pref("toolkit.osfile.log", false); - pref("toolkit.scrollbox.smoothScroll", true); pref("toolkit.scrollbox.scrollIncrement", 20); pref("toolkit.scrollbox.clickToScroll.scrollDelay", 150); // Controls logging for Sqlite.sys.mjs. pref("toolkit.sqlitejsm.loglevel", "Error"); pref("toolkit.tabbox.switchByScrolling", false);
--- a/testing/runtimes/manifest-runtimes-android.json +++ b/testing/runtimes/manifest-runtimes-android.json @@ -1559,17 +1559,16 @@ "toolkit/components/autocomplete/tests/unit/xpcshell.ini": 12.66, "toolkit/components/captivedetect/test/unit/xpcshell.ini": 6.15, "toolkit/components/cascade_bloom_filter/test/xpcshell/xpcshell.ini": 0.54, "toolkit/components/contextualidentity/tests/unit/xpcshell.ini": 1.87, "toolkit/components/extensions/test/xpcshell/xpcshell-remote.ini": 0.85, "toolkit/components/extensions/test/xpcshell/xpcshell.ini": 19.12, "toolkit/components/featuregates/test/unit/xpcshell.ini": 1.71, "toolkit/components/mozintl/test/xpcshell.ini": 1.82, - "toolkit/components/osfile/tests/xpcshell/xpcshell.ini": 27.1, "toolkit/components/telemetry/tests/unit/xpcshell.ini": 42.36, "toolkit/components/timermanager/tests/unit/xpcshell.ini": 9.07, "toolkit/components/utils/test/unit/xpcshell.ini": 2.22, "toolkit/components/windowcreator/tests/unit/xpcshell.ini": 1.15, "toolkit/content/tests/unit/xpcshell.ini": 0.53, "toolkit/modules/tests/xpcshell/xpcshell.ini": 28.47, "toolkit/mozapps/downloads/tests/unit/xpcshell.ini": 2.41, "uriloader/exthandler/tests/unit/xpcshell.ini": 5.05,
--- a/testing/runtimes/manifest-runtimes-unix.json +++ b/testing/runtimes/manifest-runtimes-unix.json @@ -381,17 +381,16 @@ "layout/xul/test/chrome.ini": 13.96, "security/manager/ssl/tests/mochitest/stricttransportsecurity/chrome.ini": 4.78, "services/fxaccounts/tests/mochitest/chrome.ini": 1.3, "testing/mochitest/chrome/chrome.ini": 2.22, "toolkit/components/aboutmemory/tests/chrome.ini": 23.09, "toolkit/components/certviewer/tests/chrome/chrome.ini": 2.78, "toolkit/components/ctypes/tests/chrome/chrome.ini": 1.4, "toolkit/components/extensions/test/mochitest/chrome.ini": 11.29, - "toolkit/components/osfile/tests/mochi/chrome.ini": 9.12, "toolkit/components/places/tests/chrome/chrome.ini": 1.87, "toolkit/components/prompts/test/chrome.ini": 106.31, "toolkit/components/resistfingerprinting/tests/chrome.ini": 3.99, "toolkit/components/url-classifier/tests/mochitest/chrome.ini": 35.15, "toolkit/components/viewsource/test/chrome.ini": 1.14, "toolkit/components/windowcreator/test/chrome.ini": 1.54, "toolkit/components/windowwatcher/test/chrome.ini": 1.32, "toolkit/components/workerloader/tests/chrome.ini": 1.19,
--- a/testing/runtimes/manifest-runtimes-windows.json +++ b/testing/runtimes/manifest-runtimes-windows.json @@ -383,17 +383,16 @@ "layout/xul/test/chrome.ini": 12.69, "security/manager/ssl/tests/mochitest/stricttransportsecurity/chrome.ini": 3.14, "services/fxaccounts/tests/mochitest/chrome.ini": 1.37, "testing/mochitest/chrome/chrome.ini": 2.22, "toolkit/components/aboutmemory/tests/chrome.ini": 24.16, "toolkit/components/certviewer/tests/chrome/chrome.ini": 2.35, "toolkit/components/ctypes/tests/chrome/chrome.ini": 1.38, "toolkit/components/extensions/test/mochitest/chrome.ini": 8.63, - "toolkit/components/osfile/tests/mochi/chrome.ini": 7.73, "toolkit/components/places/tests/chrome/chrome.ini": 1.89, "toolkit/components/prompts/test/chrome.ini": 41.2, "toolkit/components/resistfingerprinting/tests/chrome.ini": 3.59, "toolkit/components/url-classifier/tests/mochitest/chrome.ini": 22.42, "toolkit/components/viewsource/test/chrome.ini": 1.14, "toolkit/components/windowcreator/test/chrome.ini": 1.51, "toolkit/components/windowwatcher/test/chrome.ini": 1.56, "toolkit/components/workerloader/tests/chrome.ini": 1.21,
--- a/toolkit/components/build/components.conf +++ b/toolkit/components/build/components.conf @@ -70,22 +70,16 @@ Classes = [ 'cid': '{91fa9e67-1427-4ee9-8ee0-1a6ed578bee1}', 'contract_ids': ['@mozilla.org/reputationservice/login-reputation-service;1'], 'singleton': True, 'type': 'mozilla::LoginReputationService', 'headers': ['/toolkit/components/reputationservice/LoginReputation.h'], 'constructor': 'mozilla::LoginReputationService::GetSingleton', }, { - 'cid': '{63a69303-8a64-45a9-848c-d4e2792794e6}', - 'contract_ids': ['@mozilla.org/toolkit/osfile/native-internals;1'], - 'type': 'mozilla::NativeOSFileInternalsService', - 'headers': ['mozilla/NativeOSFileInternals.h'], - }, - { 'name': 'Alerts', 'cid': '{a0ccaaf8-09da-44d8-b250-9ac3e93c8117}', 'contract_ids': ['@mozilla.org/alerts-service;1'], 'type': 'nsAlertsService', 'headers': ['/toolkit/components/alerts/nsAlertsService.h'], 'overridable': True, }, {
deleted file mode 100644 --- a/toolkit/components/lz4/lz4.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "mozilla/Compression.h" - -/** - * LZ4 is a very fast byte-wise compression algorithm. - * - * Compared to Google's Snappy it is faster to compress and decompress and - * generally produces output of about the same size. - * - * Compared to zlib it compresses at about 10x the speed, decompresses at about - * 4x the speed and produces output of about 1.5x the size. - * - */ - -using namespace mozilla::Compression; - -/** - * Compresses 'inputSize' bytes from 'source' into 'dest'. - * Destination buffer must be already allocated, - * and must be sized to handle worst cases situations (input data not - * compressible) Worst case size evaluation is provided by function - * LZ4_compressBound() - * - * @param inputSize is the input size. Max supported value is ~1.9GB - * @param return the number of bytes written in buffer dest - */ -extern "C" MOZ_EXPORT size_t workerlz4_compress(const char* source, - size_t inputSize, char* dest) { - return LZ4::compress(source, inputSize, dest); -} - -/** - * If the source stream is malformed, the function will stop decoding - * and return a negative result, indicating the byte position of the - * faulty instruction - * - * This function never writes outside of provided buffers, and never - * modifies input buffer. - * - * note : destination buffer must be already allocated. - * its size must be a minimum of 'outputSize' bytes. - * @param outputSize is the output size, therefore the original size - * @return true/false - */ -extern "C" MOZ_EXPORT int workerlz4_decompress(const char* source, - size_t inputSize, char* dest, - size_t maxOutputSize, - size_t* bytesOutput) { - return LZ4::decompress(source, inputSize, dest, maxOutputSize, bytesOutput); -} - -/* - Provides the maximum size that LZ4 may output in a "worst case" - scenario (input data not compressible) primarily useful for memory - allocation of output buffer. - note : this function is limited by "int" range (2^31-1) - - @param inputSize is the input size. Max supported value is ~1.9GB - @return maximum output size in a "worst case" scenario -*/ -extern "C" MOZ_EXPORT size_t workerlz4_maxCompressedSize(size_t inputSize) { - return LZ4::maxCompressedSize(inputSize); -}
deleted file mode 100644 --- a/toolkit/components/lz4/lz4.js +++ /dev/null @@ -1,188 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -var SharedAll; -if (typeof Components != "undefined") { - SharedAll = ChromeUtils.import( - "resource://gre/modules/osfile/osfile_shared_allthreads.jsm" - ); - var { Primitives } = ChromeUtils.import( - "resource://gre/modules/lz4_internal.js" - ); - var { ctypes } = ChromeUtils.importESModule( - "resource://gre/modules/ctypes.sys.mjs" - ); - - this.EXPORTED_SYMBOLS = ["Lz4"]; - this.exports = {}; -} else if (typeof module != "undefined" && typeof require != "undefined") { - /* eslint-env commonjs */ - SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm"); - Primitives = require("resource://gre/modules/lz4_internal.js"); - ctypes = self.ctypes; -} else { - throw new Error( - "Please load this module with Component.utils.import or with require()" - ); -} - -const MAGIC_NUMBER = new Uint8Array([109, 111, 122, 76, 122, 52, 48, 0]); // "mozLz40\0" - -const BYTES_IN_SIZE_HEADER = ctypes.uint32_t.size; - -const HEADER_SIZE = MAGIC_NUMBER.byteLength + BYTES_IN_SIZE_HEADER; - -/** - * An error during (de)compression - * - * @param {string} operation The name of the operation ("compress", "decompress") - * @param {string} reason A reason to be used when matching errors. Must start - * with "because", e.g. "becauseInvalidContent". - * @param {string} message A human-readable message. - */ -function LZError(operation, reason, message) { - SharedAll.OSError.call(this); - this.operation = operation; - this[reason] = true; - this.message = message; -} -LZError.prototype = Object.create(SharedAll.OSError); -LZError.prototype.toString = function toString() { - return this.message; -}; -exports.Error = LZError; - -/** - * Compress a block to a form suitable for writing to disk. - * - * Compatibility note: For the moment, we are basing our code on lz4 - * 1.3, which does not specify a *file* format. Therefore, we define - * our own format. Once lz4 defines a complete file format, we will - * migrate both |compressFileContent| and |decompressFileContent| to this file - * format. For backwards-compatibility, |decompressFileContent| will however - * keep the ability to decompress files provided with older versions of - * |compressFileContent|. - * - * Compressed files have the following layout: - * - * | MAGIC_NUMBER (8 bytes) | content size (uint32_t, little endian) | content, as obtained from lz4_compress | - * - * @param {TypedArray|void*} buffer The buffer to write to the disk. - * @param {object=} options An object that may contain the following fields: - * - {number} bytes The number of bytes to read from |buffer|. If |buffer| - * is an |ArrayBuffer|, |bytes| defaults to |buffer.byteLength|. If - * |buffer| is a |void*|, |bytes| MUST be provided. - * @return {Uint8Array} An array of bytes suitable for being written to the - * disk. - */ -function compressFileContent(array, options = {}) { - // Prepare the output array - let inputBytes; - if (SharedAll.isTypedArray(array) && !(options && "bytes" in options)) { - inputBytes = array.byteLength; - } else if (options && options.bytes) { - inputBytes = options.bytes; - } else { - throw new TypeError("compressFileContent requires a size"); - } - let maxCompressedSize = Primitives.maxCompressedSize(inputBytes); - let outputArray = new Uint8Array(HEADER_SIZE + maxCompressedSize); - - // Compress to output array - let payload = new Uint8Array( - outputArray.buffer, - outputArray.byteOffset + HEADER_SIZE - ); - let compressedSize = Primitives.compress(array, inputBytes, payload); - - // Add headers - outputArray.set(MAGIC_NUMBER); - let view = new DataView(outputArray.buffer); - view.setUint32(MAGIC_NUMBER.byteLength, inputBytes, true); - - return new Uint8Array(outputArray.buffer, 0, HEADER_SIZE + compressedSize); -} -exports.compressFileContent = compressFileContent; - -function decompressFileContent(array, options = {}) { - let bytes = SharedAll.normalizeBufferArgs(array, options.bytes || null); - if (bytes < HEADER_SIZE) { - throw new LZError( - "decompress", - "becauseLZNoHeader", - `Buffer is too short (no header) - Data: ${options.path || array}` - ); - } - - // Read headers - let expectMagicNumber = new DataView( - array.buffer, - 0, - MAGIC_NUMBER.byteLength - ); - for (let i = 0; i < MAGIC_NUMBER.byteLength; ++i) { - if (expectMagicNumber.getUint8(i) != MAGIC_NUMBER[i]) { - throw new LZError( - "decompress", - "becauseLZWrongMagicNumber", - `Invalid header (no magic number) - Data: ${options.path || array}` - ); - } - } - - let sizeBuf = new DataView( - array.buffer, - MAGIC_NUMBER.byteLength, - BYTES_IN_SIZE_HEADER - ); - let expectDecompressedSize = - sizeBuf.getUint8(0) + - (sizeBuf.getUint8(1) << 8) + - (sizeBuf.getUint8(2) << 16) + - (sizeBuf.getUint8(3) << 24); - if (expectDecompressedSize == 0) { - // The underlying algorithm cannot handle a size of 0 - return new Uint8Array(0); - } - - // Prepare the input buffer - let inputData = new DataView(array.buffer, HEADER_SIZE); - - // Prepare the output buffer - let outputBuffer = new Uint8Array(expectDecompressedSize); - let decompressedBytes = new SharedAll.Type.size_t.implementation(0); - - // Decompress - let success = Primitives.decompress( - inputData, - bytes - HEADER_SIZE, - outputBuffer, - outputBuffer.byteLength, - decompressedBytes.address() - ); - if (!success) { - throw new LZError( - "decompress", - "becauseLZInvalidContent", - `Invalid content: Decompression stopped at ${ - decompressedBytes.value - } - Data: ${options.path || array}` - ); - } - return new Uint8Array( - outputBuffer.buffer, - outputBuffer.byteOffset, - decompressedBytes.value - ); -} -exports.decompressFileContent = decompressFileContent; - -if (typeof Components != "undefined") { - this.Lz4 = { - compressFileContent, - decompressFileContent, - }; -}
deleted file mode 100644 --- a/toolkit/components/lz4/lz4_internal.js +++ /dev/null @@ -1,76 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* eslint-env commonjs */ - -"use strict"; - -var Primitives = {}; - -var SharedAll; -if (typeof Components != "undefined") { - SharedAll = ChromeUtils.import( - "resource://gre/modules/osfile/osfile_shared_allthreads.jsm" - ); - - this.EXPORTED_SYMBOLS = ["Primitives"]; - this.Primitives = Primitives; - this.exports = {}; -} else if (typeof module != "undefined" && typeof require != "undefined") { - SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm"); -} else { - throw new Error( - "Please load this module with Component.utils.import or with require()" - ); -} - -var libxul = new SharedAll.Library("libxul", SharedAll.Constants.Path.libxul); -var Type = SharedAll.Type; - -libxul.declareLazyFFI( - Primitives, - "compress", - "workerlz4_compress", - null, - /* return*/ Type.size_t, - /* const source*/ Type.void_t.in_ptr, - /* inputSize*/ Type.size_t, - /* dest*/ Type.void_t.out_ptr -); - -libxul.declareLazyFFI( - Primitives, - "decompress", - "workerlz4_decompress", - null, - /* return*/ Type.int, - /* const source*/ Type.void_t.in_ptr, - /* inputSize*/ Type.size_t, - /* dest*/ Type.void_t.out_ptr, - /* maxOutputSize*/ Type.size_t, - /* actualOutputSize*/ Type.size_t.out_ptr -); - -libxul.declareLazyFFI( - Primitives, - "maxCompressedSize", - "workerlz4_maxCompressedSize", - null, - /* return*/ Type.size_t, - /* inputSize*/ Type.size_t -); - -if (typeof module != "undefined") { - module.exports = { - get compress() { - return Primitives.compress; - }, - get decompress() { - return Primitives.decompress; - }, - get maxCompressedSize() { - return Primitives.maxCompressedSize; - }, - }; -}
deleted file mode 100644 --- a/toolkit/components/lz4/moz.build +++ /dev/null @@ -1,21 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -with Files("**"): - BUG_COMPONENT = ("Toolkit", "OS.File") - -XPCSHELL_TESTS_MANIFESTS += ["tests/xpcshell/xpcshell.ini"] - -EXTRA_JS_MODULES += [ - "lz4.js", - "lz4_internal.js", -] - -SOURCES += [ - "lz4.cpp", -] - -FINAL_LIBRARY = "xul"
deleted file mode 100644 --- a/toolkit/components/lz4/tests/xpcshell/data/chrome.manifest +++ /dev/null @@ -1,1 +0,0 @@ -content test_lz4 ./
deleted file mode 100644 index a354edc03673f2d3fc3b9f07f9f39d1bd59bf656..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@<O00001
deleted file mode 100644 --- a/toolkit/components/lz4/tests/xpcshell/data/worker_lz4.js +++ /dev/null @@ -1,166 +0,0 @@ -/* eslint-env mozilla/chrome-worker */ - -/* import-globals-from /toolkit/components/workerloader/require.js */ -importScripts("resource://gre/modules/workers/require.js"); -/* import-globals-from /toolkit/components/osfile/osfile.jsm */ -importScripts("resource://gre/modules/osfile.jsm"); - -function info(x) { - // self.postMessage({kind: "do_print", args: [x]}); - dump("TEST-INFO: " + x + "\n"); -} - -const Assert = { - ok(x) { - self.postMessage({ kind: "assert_ok", args: [!!x] }); - if (x) { - dump("TEST-PASS: " + x + "\n"); - } else { - throw new Error("Assert.ok failed"); - } - }, - - equal(a, b) { - let result = a == b; - self.postMessage({ kind: "assert_ok", args: [result] }); - if (!result) { - throw new Error("Assert.equal failed " + a + " != " + b); - } - }, -}; - -function do_test_complete() { - self.postMessage({ kind: "do_test_complete", args: [] }); -} - -self.onmessage = function() { - try { - run_test(); - } catch (ex) { - let { message, moduleStack, moduleName, lineNumber } = ex; - let error = new Error(message, moduleName, lineNumber); - error.stack = moduleStack; - dump("Uncaught error: " + error + "\n"); - dump("Full stack: " + moduleStack + "\n"); - throw error; - } -}; - -var Lz4; -var Internals; -function test_import() { - Lz4 = require("resource://gre/modules/lz4.js"); - Internals = require("resource://gre/modules/lz4_internal.js"); -} - -function test_bound() { - for (let k of ["compress", "decompress", "maxCompressedSize"]) { - try { - info("Checking the existence of " + k + "\n"); - Assert.ok(!!Internals[k]); - info(k + " exists"); - } catch (ex) { - // Ignore errors - info(k + " doesn't exist!"); - } - } -} - -function test_reference_file() { - info("Decompress reference file"); - let path = OS.Path.join("data", "compression.lz"); - let data = OS.File.read(path); - let decompressed = Lz4.decompressFileContent(data); - let text = new TextDecoder().decode(decompressed); - Assert.equal(text, "Hello, lz4"); -} - -function compare_arrays(a, b) { - return Array.prototype.join.call(a) == Array.prototype.join.call(b); -} - -function run_rawcompression(name, array) { - info("Raw compression test " + name); - let length = array.byteLength; - let compressedArray = new Uint8Array(Internals.maxCompressedSize(length)); - let compressedBytes = Internals.compress(array, length, compressedArray); - compressedArray = new Uint8Array(compressedArray.buffer, 0, compressedBytes); - info("Raw compressed: " + length + " into " + compressedBytes); - - let decompressedArray = new Uint8Array(length); - let decompressedBytes = new ctypes.size_t(); - let success = Internals.decompress( - compressedArray, - compressedBytes, - decompressedArray, - length, - decompressedBytes.address() - ); - info("Raw decompression success? " + success); - info("Raw decompression size: " + decompressedBytes.value); - Assert.ok(compare_arrays(array, decompressedArray)); -} - -function run_filecompression(name, array) { - info("File compression test " + name); - let compressed = Lz4.compressFileContent(array); - info( - "Compressed " + array.byteLength + " bytes into " + compressed.byteLength - ); - - let decompressed = Lz4.decompressFileContent(compressed); - info( - "Decompressed " + - compressed.byteLength + - " bytes into " + - decompressed.byteLength - ); - Assert.ok(compare_arrays(array, decompressed)); -} - -function run_faileddecompression(name, array) { - info("invalid decompression test " + name); - - // Ensure that raw decompression doesn't segfault - let length = 1 << 14; - let decompressedArray = new Uint8Array(length); - let decompressedBytes = new ctypes.size_t(); - Internals.decompress( - array, - array.byteLength, - decompressedArray, - length, - decompressedBytes.address() - ); - - // File decompression should fail with an acceptable exception - let exn = null; - try { - Lz4.decompressFileContent(array); - } catch (ex) { - exn = ex; - } - Assert.ok(exn); - if (array.byteLength < 10) { - Assert.ok(exn.becauseLZNoHeader); - } else { - Assert.ok(exn.becauseLZWrongMagicNumber); - } -} - -function run_test() { - test_import(); - test_bound(); - test_reference_file(); - for (let length of [0, 1, 1024]) { - let array = new Uint8Array(length); - for (let i = 0; i < length; ++i) { - array[i] = i % 256; - } - let name = length + " bytes"; - run_rawcompression(name, array); - run_filecompression(name, array); - run_faileddecompression(name, array); - } - do_test_complete(); -}
deleted file mode 100644 --- a/toolkit/components/lz4/tests/xpcshell/test_lz4.js +++ /dev/null @@ -1,35 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -var WORKER_SOURCE_URI = "chrome://test_lz4/content/worker_lz4.js"; -do_load_manifest("data/chrome.manifest"); - -add_task(function() { - let worker = new ChromeWorker(WORKER_SOURCE_URI); - return new Promise((resolve, reject) => { - worker.onmessage = function(event) { - let data = event.data; - switch (data.kind) { - case "assert_ok": - try { - Assert.ok(data.args[0]); - } catch (ex) { - // Ignore errors - } - return; - case "do_test_complete": - resolve(); - worker.terminate(); - break; - case "do_print": - info(data.args[0]); - } - }; - worker.onerror = function(event) { - let error = new Error(event.message, event.filename, event.lineno); - worker.terminate(); - reject(error); - }; - worker.postMessage("START"); - }); -});
deleted file mode 100644 --- a/toolkit/components/lz4/tests/xpcshell/test_lz4_sync.js +++ /dev/null @@ -1,41 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -const { Lz4 } = ChromeUtils.import("resource://gre/modules/lz4.js"); -const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -function compare_arrays(a, b) { - return Array.prototype.join.call(a) == Array.prototype.join.call(b); -} - -add_task(async function() { - let path = OS.Path.join("data", "compression.lz"); - let data = await OS.File.read(path); - let decompressed = Lz4.decompressFileContent(data); - let text = new TextDecoder().decode(decompressed); - Assert.equal(text, "Hello, lz4"); -}); - -add_task(async function() { - for (let length of [0, 1, 1024]) { - let array = new Uint8Array(length); - for (let i = 0; i < length; ++i) { - array[i] = i % 256; - } - - let compressed = Lz4.compressFileContent(array); - info( - "Compressed " + array.byteLength + " bytes into " + compressed.byteLength - ); - - let decompressed = Lz4.decompressFileContent(compressed); - info( - "Decompressed " + - compressed.byteLength + - " bytes into " + - decompressed.byteLength - ); - - Assert.ok(compare_arrays(array, decompressed)); - } -});
deleted file mode 100644 --- a/toolkit/components/lz4/tests/xpcshell/xpcshell.ini +++ /dev/null @@ -1,10 +0,0 @@ -[DEFAULT] -head = -skip-if = toolkit == 'android' -support-files = - data/worker_lz4.js - data/chrome.manifest - data/compression.lz - -[test_lz4.js] -[test_lz4_sync.js]
--- a/toolkit/components/moz.build +++ b/toolkit/components/moz.build @@ -41,21 +41,19 @@ DIRS += [ "formautofill", "finalizationwitness", "find", "forgetaboutsite", "glean", "httpsonlyerror", "jsoncpp/src/lib_json", "kvstore", - "lz4", "mediasniffer", "mozintl", "mozprotocol", - "osfile", "parentalcontrols", "passwordmgr", "pdfjs", "perfmonitoring", "pictureinpicture", "places", "processtools", "processsingleton",
deleted file mode 100644 --- a/toolkit/components/osfile/NativeOSFileInternals.cpp +++ /dev/null @@ -1,1263 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Native implementation of some OS.File operations. - */ - -#include "NativeOSFileInternals.h" - -#include "nsString.h" -#include "nsNetCID.h" -#include "nsThreadUtils.h" -#include "nsCycleCollectionParticipant.h" -#include "nsServiceManagerUtils.h" -#include "nsProxyRelease.h" - -#include "mozilla/dom/NativeOSFileInternalsBinding.h" - -#include "mozilla/Encoding.h" -#include "nsIEventTarget.h" - -#include "mozilla/DebugOnly.h" -#include "mozilla/Scoped.h" -#include "mozilla/HoldDropJSObjects.h" -#include "mozilla/TimeStamp.h" -#include "mozilla/UniquePtr.h" - -#include "prio.h" -#include "prerror.h" -#include "private/pprio.h" - -#include "jsapi.h" -#include "jsfriendapi.h" -#include "js/ArrayBuffer.h" // JS::GetArrayBufferByteLength,IsArrayBufferObject,NewArrayBufferWithContents,StealArrayBufferContents -#include "js/experimental/TypedData.h" // JS_NewUint8ArrayWithBuffer -#include "js/Utility.h" -#include "xpcpublic.h" - -#include <algorithm> -#if defined(XP_UNIX) -# include <errno.h> -#endif // defined (XP_UNIX) - -#if defined(XP_WIN) -# include <windows.h> -#endif // defined (XP_WIN) - -namespace mozilla { - -MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, PRFileDesc, - PR_Close) - -namespace { - -// Utilities for safely manipulating ArrayBuffer contents even in the -// absence of a JSContext. - -/** - * The C buffer underlying to an ArrayBuffer. Throughout the code, we manipulate - * this instead of a void* buffer, as this lets us transfer data across threads - * and into JavaScript without copy. - */ -struct ArrayBufferContents { - /** - * The data of the ArrayBuffer. This is the pointer manipulated to - * read/write the contents of the buffer. - */ - uint8_t* data; - /** - * The number of bytes in the ArrayBuffer. - */ - size_t nbytes; -}; - -/** - * RAII for ArrayBufferContents. - */ -struct ScopedArrayBufferContentsTraits { - typedef ArrayBufferContents type; - const static type empty() { - type result = {0, 0}; - return result; - } - static void release(type ptr) { - js_free(ptr.data); - ptr.data = nullptr; - ptr.nbytes = 0; - } -}; - -struct MOZ_NON_TEMPORARY_CLASS ScopedArrayBufferContents - : public Scoped<ScopedArrayBufferContentsTraits> { - explicit ScopedArrayBufferContents() - : Scoped<ScopedArrayBufferContentsTraits>() {} - - ScopedArrayBufferContents& operator=(ArrayBufferContents ptr) { - Scoped<ScopedArrayBufferContentsTraits>::operator=(ptr); - return *this; - } - - /** - * Request memory for this ArrayBufferContent. This memory may later - * be used to create an ArrayBuffer object (possibly on another - * thread) without copy. - * - * @return true In case of success, false otherwise. - */ - bool Allocate(uint32_t length) { - dispose(); - ArrayBufferContents& value = rwget(); - void* ptr = js_calloc(1, length); - if (ptr) { - value.data = (uint8_t*)ptr; - value.nbytes = length; - return true; - } - return false; - } - - private: - explicit ScopedArrayBufferContents(ScopedArrayBufferContents& source) = - delete; - ScopedArrayBufferContents& operator=(ScopedArrayBufferContents& source) = - delete; -}; - -///////// Cross-platform issues - -// Platform specific constants. As OS.File always uses OS-level -// errors, we need to map a few high-level errors to OS-level -// constants. -#if defined(XP_UNIX) -# define OS_ERROR_FILE_EXISTS EEXIST -# define OS_ERROR_NOMEM ENOMEM -# define OS_ERROR_INVAL EINVAL -# define OS_ERROR_TOO_LARGE EFBIG -# define OS_ERROR_RACE EIO -#elif defined(XP_WIN) -# define OS_ERROR_FILE_EXISTS ERROR_ALREADY_EXISTS -# define OS_ERROR_NOMEM ERROR_NOT_ENOUGH_MEMORY -# define OS_ERROR_INVAL ERROR_BAD_ARGUMENTS -# define OS_ERROR_TOO_LARGE ERROR_FILE_TOO_LARGE -# define OS_ERROR_RACE ERROR_SHARING_VIOLATION -#else -# error "We do not have platform-specific constants for this platform" -#endif - -///////// Results of OS.File operations - -/** - * Base class for results passed to the callbacks. - * - * This base class implements caching of JS values returned to the client. - * We make use of this caching in derived classes e.g. to avoid accidents - * when we transfer data allocated on another thread into JS. Note that - * this caching can lead to cycles (e.g. if a client adds a back-reference - * in the JS value), so we implement all Cycle Collector primitives in - * AbstractResult. - */ -class AbstractResult : public nsINativeOSFileResult { - public: - NS_DECL_NSINATIVEOSFILERESULT - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AbstractResult) - - /** - * Construct the result object. Must be called on the main thread - * as the AbstractResult is cycle-collected. - * - * @param aStartDate The instant at which the operation was - * requested. Used to collect Telemetry statistics. - */ - explicit AbstractResult(TimeStamp aStartDate) : mStartDate(aStartDate) { - MOZ_ASSERT(NS_IsMainThread()); - mozilla::HoldJSObjects(this); - } - - /** - * Setup the AbstractResult once data is available. - * - * @param aDispatchDate The instant at which the IO thread received - * the operation request. Used to collect Telemetry statistics. - * @param aExecutionDuration The duration of the operation on the - * IO thread. - */ - void Init(TimeStamp aDispatchDate, TimeDuration aExecutionDuration) { - MOZ_ASSERT(!NS_IsMainThread()); - - mDispatchDuration = (aDispatchDate - mStartDate); - mExecutionDuration = aExecutionDuration; - } - - /** - * Drop any data that could lead to a cycle. - */ - void DropJSData() { mCachedResult = JS::UndefinedValue(); } - - protected: - virtual ~AbstractResult() { - MOZ_ASSERT(NS_IsMainThread()); - mozilla::DropJSObjects(this); - } - - virtual nsresult GetCacheableResult(JSContext* cx, - JS::MutableHandle<JS::Value> aResult) = 0; - - private: - TimeStamp mStartDate; - TimeDuration mDispatchDuration; - TimeDuration mExecutionDuration; - JS::Heap<JS::Value> mCachedResult; -}; - -NS_IMPL_CYCLE_COLLECTING_ADDREF(AbstractResult) -NS_IMPL_CYCLE_COLLECTING_RELEASE(AbstractResult) - -NS_IMPL_CYCLE_COLLECTION_CLASS(AbstractResult) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AbstractResult) - NS_INTERFACE_MAP_ENTRY(nsINativeOSFileResult) - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AbstractResult) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedResult) -NS_IMPL_CYCLE_COLLECTION_TRACE_END - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AbstractResult) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AbstractResult) - tmp->DropJSData(); -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMETHODIMP -AbstractResult::GetDispatchDurationMS(double* aDispatchDuration) { - *aDispatchDuration = mDispatchDuration.ToMilliseconds(); - return NS_OK; -} - -NS_IMETHODIMP -AbstractResult::GetExecutionDurationMS(double* aExecutionDuration) { - *aExecutionDuration = mExecutionDuration.ToMilliseconds(); - return NS_OK; -} - -NS_IMETHODIMP -AbstractResult::GetResult(JSContext* cx, JS::MutableHandle<JS::Value> aResult) { - if (mCachedResult.isUndefined()) { - nsresult rv = GetCacheableResult(cx, aResult); - if (NS_FAILED(rv)) { - return rv; - } - mCachedResult = aResult; - return NS_OK; - } - aResult.set(mCachedResult); - return NS_OK; -} - -/** - * Return a result as a string. - * - * In this implementation, attribute |result| is a string. Strings are - * passed to JS without copy. - */ -class StringResult final : public AbstractResult { - public: - explicit StringResult(TimeStamp aStartDate) : AbstractResult(aStartDate) {} - - /** - * Initialize the object once the contents of the result as available. - * - * @param aContents The string to pass to JavaScript. Ownership of the - * string and its contents is passed to StringResult. The string must - * be valid UTF-16. - */ - void Init(TimeStamp aDispatchDate, TimeDuration aExecutionDuration, - nsString& aContents) { - AbstractResult::Init(aDispatchDate, aExecutionDuration); - mContents = aContents; - } - - protected: - nsresult GetCacheableResult(JSContext* cx, - JS::MutableHandle<JS::Value> aResult) override; - - private: - nsString mContents; -}; - -nsresult StringResult::GetCacheableResult( - JSContext* cx, JS::MutableHandle<JS::Value> aResult) { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mContents.get()); - - // Convert mContents to a js string without copy. Note that this - // may have the side-effect of stealing the contents of the string - // from XPCOM and into JS. - if (!xpc::StringToJsval(cx, mContents, aResult)) { - return NS_ERROR_FAILURE; - } - return NS_OK; -} - -/** - * Return a result as a Uint8Array. - * - * In this implementation, attribute |result| is a Uint8Array. The array - * is passed to JS without memory copy. - */ -class TypedArrayResult final : public AbstractResult { - public: - explicit TypedArrayResult(TimeStamp aStartDate) - : AbstractResult(aStartDate) {} - - /** - * @param aContents The contents to pass to JS. Calling this method. - * transmits ownership of the ArrayBufferContents to the TypedArrayResult. - * Do not reuse this value anywhere else. - */ - void Init(TimeStamp aDispatchDate, TimeDuration aExecutionDuration, - ArrayBufferContents aContents) { - AbstractResult::Init(aDispatchDate, aExecutionDuration); - mContents = aContents; - } - - protected: - nsresult GetCacheableResult(JSContext* cx, - JS::MutableHandle<JS::Value> aResult) override; - - private: - ScopedArrayBufferContents mContents; -}; - -nsresult TypedArrayResult::GetCacheableResult( - JSContext* cx, JS::MutableHandle<JS::Value> aResult) { - MOZ_ASSERT(NS_IsMainThread()); - // We cannot simply construct a typed array using contents.data as - // this would allow us to have several otherwise unrelated - // ArrayBuffers with the same underlying C buffer. As this would be - // very unsafe, we need to cache the result once we have it. - - const ArrayBufferContents& contents = mContents.get(); - MOZ_ASSERT(contents.data); - - // This takes ownership of the buffer and notes the memory allocation. - JS::Rooted<JSObject*> arrayBuffer( - cx, JS::NewArrayBufferWithContents(cx, contents.nbytes, contents.data)); - if (!arrayBuffer) { - return NS_ERROR_OUT_OF_MEMORY; - } - - JS::Rooted<JSObject*> result( - cx, JS_NewUint8ArrayWithBuffer(cx, arrayBuffer, 0, contents.nbytes)); - if (!result) { - return NS_ERROR_OUT_OF_MEMORY; - } - mContents.forget(); - - aResult.setObject(*result); - return NS_OK; -} - -/** - * Return a result as an int32_t. - * - * In this implementation, attribute |result| is an int32_t. - */ -class Int32Result final : public AbstractResult { - public: - explicit Int32Result(TimeStamp aStartDate) - : AbstractResult(aStartDate), mContents(0) {} - - /** - * Initialize the object once the contents of the result are available. - * - * @param aContents The contents to pass to JS. This is an int32_t. - */ - void Init(TimeStamp aDispatchDate, TimeDuration aExecutionDuration, - int32_t aContents) { - AbstractResult::Init(aDispatchDate, aExecutionDuration); - mContents = aContents; - } - - protected: - nsresult GetCacheableResult(JSContext* cx, - JS::MutableHandle<JS::Value> aResult) override; - - private: - int32_t mContents; -}; - -nsresult Int32Result::GetCacheableResult(JSContext* cx, - JS::MutableHandle<JS::Value> aResult) { - MOZ_ASSERT(NS_IsMainThread()); - aResult.set(JS::NumberValue(mContents)); - return NS_OK; -} - -//////// Callback events - -/** - * An event used to notify asynchronously of an error. - */ -class OSFileErrorEvent final : public Runnable { - public: - /** - * @param aOnSuccess The success callback. - * @param aOnError The error callback. - * @param aDiscardedResult The discarded result. - * @param aOperation The name of the operation, used for error reporting. - * @param aOSError The OS error of the operation, as returned by errno/ - * GetLastError(). - * - * Note that we pass both the success callback and the error - * callback, as well as the discarded result to ensure that they are - * all released on the main thread, rather than on the IO thread - * (which would hopefully segfault). Also, we pass the callbacks as - * alread_AddRefed to ensure that we do not manipulate main-thread - * only refcounters off the main thread. - */ - OSFileErrorEvent( - nsMainThreadPtrHandle<nsINativeOSFileSuccessCallback>& aOnSuccess, - nsMainThreadPtrHandle<nsINativeOSFileErrorCallback>& aOnError, - already_AddRefed<AbstractResult>& aDiscardedResult, - const nsACString& aOperation, int32_t aOSError) - : Runnable("OSFileErrorEvent"), - mOnSuccess(aOnSuccess), - mOnError(aOnError), - mDiscardedResult(aDiscardedResult), - mOSError(aOSError), - mOperation(aOperation) { - MOZ_ASSERT(!NS_IsMainThread()); - } - - NS_IMETHOD Run() override { - MOZ_ASSERT(NS_IsMainThread()); - (void)mOnError->Complete(mOperation, mOSError); - - // Ensure that the callbacks are released on the main thread. - mOnSuccess = nullptr; - mOnError = nullptr; - mDiscardedResult = nullptr; - - return NS_OK; - } - - private: - // The callbacks. Maintained as nsMainThreadPtrHandle as they are generally - // xpconnect values, which cannot be manipulated with nsCOMPtr off - // the main thread. We store both the success callback and the - // error callback to ensure that they are safely released on the - // main thread. - nsMainThreadPtrHandle<nsINativeOSFileSuccessCallback> mOnSuccess; - nsMainThreadPtrHandle<nsINativeOSFileErrorCallback> mOnError; - RefPtr<AbstractResult> mDiscardedResult; - int32_t mOSError; - nsCString mOperation; -}; - -/** - * An event used to notify of a success. - */ -class SuccessEvent final : public Runnable { - public: - /** - * @param aOnSuccess The success callback. - * @param aOnError The error callback. - * - * Note that we pass both the success callback and the error - * callback to ensure that they are both released on the main - * thread, rather than on the IO thread (which would hopefully - * segfault). Also, we pass them as alread_AddRefed to ensure that - * we do not manipulate xpconnect refcounters off the main thread - * (which is illegal). - */ - SuccessEvent( - nsMainThreadPtrHandle<nsINativeOSFileSuccessCallback>& aOnSuccess, - nsMainThreadPtrHandle<nsINativeOSFileErrorCallback>& aOnError, - already_AddRefed<nsINativeOSFileResult>& aResult) - : Runnable("SuccessEvent"), - mOnSuccess(aOnSuccess), - mOnError(aOnError), - mResult(aResult) { - MOZ_ASSERT(!NS_IsMainThread()); - } - - NS_IMETHOD Run() override { - MOZ_ASSERT(NS_IsMainThread()); - (void)mOnSuccess->Complete(mResult); - - // Ensure that the callbacks are released on the main thread. - mOnSuccess = nullptr; - mOnError = nullptr; - mResult = nullptr; - - return NS_OK; - } - - private: - // The callbacks. Maintained as nsMainThreadPtrHandle as they are generally - // xpconnect values, which cannot be manipulated with nsCOMPtr off - // the main thread. We store both the success callback and the - // error callback to ensure that they are safely released on the - // main thread. - nsMainThreadPtrHandle<nsINativeOSFileSuccessCallback> mOnSuccess; - nsMainThreadPtrHandle<nsINativeOSFileErrorCallback> mOnError; - RefPtr<nsINativeOSFileResult> mResult; -}; - -//////// Action events - -/** - * Base class shared by actions. - */ -class AbstractDoEvent : public Runnable { - public: - AbstractDoEvent( - nsMainThreadPtrHandle<nsINativeOSFileSuccessCallback>& aOnSuccess, - nsMainThreadPtrHandle<nsINativeOSFileErrorCallback>& aOnError) - : Runnable("AbstractDoEvent"), - mOnSuccess(aOnSuccess), - mOnError(aOnError) -#if defined(DEBUG) - , - mResolved(false) -#endif // defined(DEBUG) - { - MOZ_ASSERT(NS_IsMainThread()); - } - - /** - * Fail, asynchronously. - */ - void Fail(const nsACString& aOperation, - already_AddRefed<AbstractResult>&& aDiscardedResult, - int32_t aOSError = 0) { - Resolve(); - - RefPtr<OSFileErrorEvent> event = new OSFileErrorEvent( - mOnSuccess, mOnError, aDiscardedResult, aOperation, aOSError); - nsresult rv = NS_DispatchToMainThread(event); - if (NS_FAILED(rv)) { - // Last ditch attempt to release on the main thread - some of - // the members of event are not thread-safe, so letting the - // pointer go out of scope would cause a crash. - NS_ReleaseOnMainThread("AbstractDoEvent::OSFileErrorEvent", - event.forget()); - } - } - - /** - * Succeed, asynchronously. - */ - void Succeed(already_AddRefed<nsINativeOSFileResult>&& aResult) { - Resolve(); - RefPtr<SuccessEvent> event = - new SuccessEvent(mOnSuccess, mOnError, aResult); - nsresult rv = NS_DispatchToMainThread(event); - if (NS_FAILED(rv)) { - // Last ditch attempt to release on the main thread - some of - // the members of event are not thread-safe, so letting the - // pointer go out of scope would cause a crash. - NS_ReleaseOnMainThread("AbstractDoEvent::SuccessEvent", event.forget()); - } - } - - private: - /** - * Mark the event as complete, for debugging purposes. - */ - void Resolve() { -#if defined(DEBUG) - MOZ_ASSERT(!mResolved); - mResolved = true; -#endif // defined(DEBUG) - } - - private: - nsMainThreadPtrHandle<nsINativeOSFileSuccessCallback> mOnSuccess; - nsMainThreadPtrHandle<nsINativeOSFileErrorCallback> mOnError; -#if defined(DEBUG) - // |true| once the action is complete - bool mResolved; -#endif // defined(DEBUG) -}; - -/** - * An abstract event implementing reading from a file. - * - * Concrete subclasses are responsible for handling the - * data obtained from the file and possibly post-processing it. - */ -class AbstractReadEvent : public AbstractDoEvent { - public: - /** - * @param aPath The path of the file. - */ - AbstractReadEvent( - const nsAString& aPath, const uint64_t aBytes, - nsMainThreadPtrHandle<nsINativeOSFileSuccessCallback>& aOnSuccess, - nsMainThreadPtrHandle<nsINativeOSFileErrorCallback>& aOnError) - : AbstractDoEvent(aOnSuccess, aOnError), mPath(aPath), mBytes(aBytes) { - MOZ_ASSERT(NS_IsMainThread()); - } - - NS_IMETHOD Run() override { - MOZ_ASSERT(!NS_IsMainThread()); - TimeStamp dispatchDate = TimeStamp::Now(); - - nsresult rv = BeforeRead(); - if (NS_FAILED(rv)) { - // Error reporting is handled by BeforeRead(); - return NS_OK; - } - - ScopedArrayBufferContents buffer; - rv = Read(buffer); - if (NS_FAILED(rv)) { - // Error reporting is handled by Read(); - return NS_OK; - } - - AfterRead(dispatchDate, buffer); - return NS_OK; - } - - private: - /** - * Read synchronously. - * - * Must be called off the main thread. - * - * @param aBuffer The destination buffer. - */ - nsresult Read(ScopedArrayBufferContents& aBuffer) { - MOZ_ASSERT(!NS_IsMainThread()); - - ScopedPRFileDesc file; -#if defined(XP_WIN) - // On Windows, we can't use PR_OpenFile because it doesn't - // handle UTF-16 encoding, which is pretty bad. In addition, - // PR_OpenFile opens files without sharing, which is not the - // general semantics of OS.File. - HANDLE handle = - ::CreateFileW(mPath.get(), GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - /*Security attributes*/ nullptr, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, - /*Template file*/ nullptr); - - if (handle == INVALID_HANDLE_VALUE) { - Fail("open"_ns, nullptr, ::GetLastError()); - return NS_ERROR_FAILURE; - } - - file = PR_ImportFile((PROsfd)handle); - if (!file) { - // |file| is closed by PR_ImportFile - Fail("ImportFile"_ns, nullptr, PR_GetOSError()); - return NS_ERROR_FAILURE; - } - -#else - // On other platforms, PR_OpenFile will do. - NS_ConvertUTF16toUTF8 path(mPath); - file = PR_OpenFile(path.get(), PR_RDONLY, 0); - if (!file) { - Fail("open"_ns, nullptr, PR_GetOSError()); - return NS_ERROR_FAILURE; - } - -#endif // defined(XP_XIN) - - PRFileInfo64 stat; - if (PR_GetOpenFileInfo64(file, &stat) != PR_SUCCESS) { - Fail("stat"_ns, nullptr, PR_GetOSError()); - return NS_ERROR_FAILURE; - } - - uint64_t bytes = std::min((uint64_t)stat.size, mBytes); - if (bytes > UINT32_MAX) { - Fail("Arithmetics"_ns, nullptr, OS_ERROR_INVAL); - return NS_ERROR_FAILURE; - } - - if (!aBuffer.Allocate(bytes)) { - Fail("allocate"_ns, nullptr, OS_ERROR_NOMEM); - return NS_ERROR_FAILURE; - } - - uint64_t total_read = 0; - int32_t just_read = 0; - char* dest_chars = reinterpret_cast<char*>(aBuffer.rwget().data); - do { - just_read = PR_Read(file, dest_chars + total_read, - std::min(uint64_t(PR_INT32_MAX), bytes - total_read)); - if (just_read == -1) { - Fail("read"_ns, nullptr, PR_GetOSError()); - return NS_ERROR_FAILURE; - } - total_read += just_read; - } while (just_read != 0 && total_read < bytes); - if (total_read != bytes) { - // We seem to have a race condition here. - Fail("read"_ns, nullptr, OS_ERROR_RACE); - return NS_ERROR_FAILURE; - } - - return NS_OK; - } - - protected: - /** - * Any steps that need to be taken before reading. - * - * In case of error, this method should call Fail() and return - * a failure code. - */ - virtual nsresult BeforeRead() { return NS_OK; } - - /** - * Proceed after reading. - */ - virtual void AfterRead(TimeStamp aDispatchDate, - ScopedArrayBufferContents& aBuffer) = 0; - - protected: - const nsString mPath; - const uint64_t mBytes; -}; - -/** - * An implementation of a Read event that provides the data - * as a TypedArray. - */ -class DoReadToTypedArrayEvent final : public AbstractReadEvent { - public: - DoReadToTypedArrayEvent( - const nsAString& aPath, const uint32_t aBytes, - nsMainThreadPtrHandle<nsINativeOSFileSuccessCallback>& aOnSuccess, - nsMainThreadPtrHandle<nsINativeOSFileErrorCallback>& aOnError) - : AbstractReadEvent(aPath, aBytes, aOnSuccess, aOnError), - mResult(new TypedArrayResult(TimeStamp::Now())) {} - - ~DoReadToTypedArrayEvent() override { - // If AbstractReadEvent::Run() has bailed out, we may need to cleanup - // mResult, which is main-thread only data - if (!mResult) { - return; - } - NS_ReleaseOnMainThread("DoReadToTypedArrayEvent::mResult", - mResult.forget()); - } - - protected: - void AfterRead(TimeStamp aDispatchDate, - ScopedArrayBufferContents& aBuffer) override { - MOZ_ASSERT(!NS_IsMainThread()); - mResult->Init(aDispatchDate, TimeStamp::Now() - aDispatchDate, - aBuffer.forget()); - Succeed(mResult.forget()); - } - - private: - RefPtr<TypedArrayResult> mResult; -}; - -/** - * An implementation of a Read event that provides the data - * as a JavaScript string. - */ -class DoReadToStringEvent final : public AbstractReadEvent { - public: - DoReadToStringEvent( - const nsAString& aPath, const nsACString& aEncoding, - const uint32_t aBytes, - nsMainThreadPtrHandle<nsINativeOSFileSuccessCallback>& aOnSuccess, - nsMainThreadPtrHandle<nsINativeOSFileErrorCallback>& aOnError) - : AbstractReadEvent(aPath, aBytes, aOnSuccess, aOnError), - mEncoding(aEncoding), - mResult(new StringResult(TimeStamp::Now())) {} - - ~DoReadToStringEvent() override { - // If AbstraactReadEvent::Run() has bailed out, we may need to cleanup - // mResult, which is main-thread only data - if (!mResult) { - return; - } - NS_ReleaseOnMainThread("DoReadToStringEvent::mResult", mResult.forget()); - } - - protected: - nsresult BeforeRead() override { - // Obtain the decoder. We do this before reading to avoid doing - // any unnecessary I/O in case the name of the encoding is incorrect. - MOZ_ASSERT(!NS_IsMainThread()); - const Encoding* encoding = Encoding::ForLabel(mEncoding); - if (!encoding) { - Fail("Decode"_ns, mResult.forget(), OS_ERROR_INVAL); - return NS_ERROR_FAILURE; - } - mDecoder = encoding->NewDecoderWithBOMRemoval(); - if (!mDecoder) { - Fail("DecoderForEncoding"_ns, mResult.forget(), OS_ERROR_INVAL); - return NS_ERROR_FAILURE; - } - - return NS_OK; - } - - void AfterRead(TimeStamp aDispatchDate, - ScopedArrayBufferContents& aBuffer) override { - MOZ_ASSERT(!NS_IsMainThread()); - - auto src = Span(aBuffer.get().data, aBuffer.get().nbytes); - - CheckedInt<size_t> needed = mDecoder->MaxUTF16BufferLength(src.Length()); - if (!needed.isValid() || - needed.value() > std::numeric_limits<nsAString::size_type>::max()) { - Fail("arithmetics"_ns, mResult.forget(), OS_ERROR_TOO_LARGE); - return; - } - - nsString resultString; - auto resultSpan = resultString.GetMutableData(needed.value(), fallible); - if (!resultSpan) { - Fail("allocation"_ns, mResult.forget(), OS_ERROR_TOO_LARGE); - return; - } - - // Yoric said on IRC that this method is normally called for the entire - // file, but that's not guaranteed. Retaining the bug that EOF in conversion - // isn't handled anywhere. - uint32_t result; - size_t read; - size_t written; - std::tie(result, read, written, std::ignore) = - mDecoder->DecodeToUTF16(src, *resultSpan, false); - MOZ_ASSERT(result == kInputEmpty); - MOZ_ASSERT(read == src.Length()); - MOZ_ASSERT(written <= needed.value()); - bool ok = resultString.SetLength(written, fallible); - if (!ok) { - Fail("allocation"_ns, mResult.forget(), OS_ERROR_TOO_LARGE); - return; - } - - mResult->Init(aDispatchDate, TimeStamp::Now() - aDispatchDate, - resultString); - Succeed(mResult.forget()); - } - - private: - nsCString mEncoding; - mozilla::UniquePtr<mozilla::Decoder> mDecoder; - RefPtr<StringResult> mResult; -}; - -/** - * An event implenting writing atomically to a file. - */ -class DoWriteAtomicEvent : public AbstractDoEvent { - public: - /** - * @param aPath The path of the file. - */ - DoWriteAtomicEvent( - const nsAString& aPath, UniquePtr<char[], JS::FreePolicy> aBuffer, - const uint64_t aBytes, const nsAString& aTmpPath, - const nsAString& aBackupTo, const bool aFlush, const bool aNoOverwrite, - nsMainThreadPtrHandle<nsINativeOSFileSuccessCallback>& aOnSuccess, - nsMainThreadPtrHandle<nsINativeOSFileErrorCallback>& aOnError) - : AbstractDoEvent(aOnSuccess, aOnError), - mPath(aPath), - mBuffer(std::move(aBuffer)), - mBytes(aBytes), - mTmpPath(aTmpPath), - mBackupTo(aBackupTo), - mFlush(aFlush), - mNoOverwrite(aNoOverwrite), - mResult(new Int32Result(TimeStamp::Now())) { - MOZ_ASSERT(NS_IsMainThread()); - } - - ~DoWriteAtomicEvent() override { - // If Run() has bailed out, we may need to cleanup - // mResult, which is main-thread only data - if (!mResult) { - return; - } - NS_ReleaseOnMainThread("DoWriteAtomicEvent::mResult", mResult.forget()); - } - - NS_IMETHODIMP Run() override { - MOZ_ASSERT(!NS_IsMainThread()); - TimeStamp dispatchDate = TimeStamp::Now(); - int32_t bytesWritten; - - nsresult rv = WriteAtomic(&bytesWritten); - if (NS_FAILED(rv)) { - return NS_OK; - } - - AfterWriteAtomic(dispatchDate, bytesWritten); - return NS_OK; - } - - private: - /** - * Write atomically to a file. - * Must be called off the main thread. - * @param aBytesWritten will contain the total bytes written. - * This does not support compression in this implementation. - */ - nsresult WriteAtomic(int32_t* aBytesWritten) { - MOZ_ASSERT(!NS_IsMainThread()); - - // Note: In Windows, many NSPR File I/O functions which act on pathnames - // do not handle UTF-16 encoding. Thus, we use the following functions - // to overcome this. - // PR_Access : GetFileAttributesW - // PR_Delete : DeleteFileW - // PR_OpenFile : CreateFileW followed by PR_ImportFile - // PR_Rename : MoveFileW - - ScopedPRFileDesc file; - NS_ConvertUTF16toUTF8 path(mPath); - NS_ConvertUTF16toUTF8 tmpPath(mTmpPath); - NS_ConvertUTF16toUTF8 backupTo(mBackupTo); - bool fileExists = false; - - if (!mTmpPath.IsVoid() || !mBackupTo.IsVoid() || mNoOverwrite) { - // fileExists needs to be computed in the case of tmpPath, since - // the rename behaves differently depending on whether the - // file already exists. It's also computed for backupTo since the - // backup can be skipped if the file does not exist in the first place. -#if defined(XP_WIN) - fileExists = ::GetFileAttributesW(mPath.get()) != INVALID_FILE_ATTRIBUTES; -#else - fileExists = PR_Access(path.get(), PR_ACCESS_EXISTS) == PR_SUCCESS; -#endif // defined(XP_WIN) - } - - // Check noOverwrite. - if (mNoOverwrite && fileExists) { - Fail("noOverwrite"_ns, nullptr, OS_ERROR_FILE_EXISTS); - return NS_ERROR_FAILURE; - } - - // Backup the original file if it exists. - if (!mBackupTo.IsVoid() && fileExists) { -#if defined(XP_WIN) - if (::GetFileAttributesW(mBackupTo.get()) != INVALID_FILE_ATTRIBUTES) { - // The file specified by mBackupTo exists, so we need to delete it - // first. - if (::DeleteFileW(mBackupTo.get()) == false) { - Fail("delete"_ns, nullptr, ::GetLastError()); - return NS_ERROR_FAILURE; - } - } - - if (::MoveFileW(mPath.get(), mBackupTo.get()) == false) { - Fail("rename"_ns, nullptr, ::GetLastError()); - return NS_ERROR_FAILURE; - } -#else - if (PR_Access(backupTo.get(), PR_ACCESS_EXISTS) == PR_SUCCESS) { - // The file specified by mBackupTo exists, so we need to delete it - // first. - if (PR_Delete(backupTo.get()) == PR_FAILURE) { - Fail("delete"_ns, nullptr, PR_GetOSError()); - return NS_ERROR_FAILURE; - } - } - - if (PR_Rename(path.get(), backupTo.get()) == PR_FAILURE) { - Fail("rename"_ns, nullptr, PR_GetOSError()); - return NS_ERROR_FAILURE; - } -#endif // defined(XP_WIN) - } - -#if defined(XP_WIN) - // In addition to not handling UTF-16 encoding in file paths, - // PR_OpenFile opens files without sharing, which is not the - // general semantics of OS.File. - HANDLE handle; - // if we're dealing with a tmpFile, we need to write there. - if (!mTmpPath.IsVoid()) { - handle = ::CreateFileW( - mTmpPath.get(), GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - /*Security attributes*/ nullptr, - // CREATE_ALWAYS is used since since we need to create the temporary - // file, which we don't care about overwriting. - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, - /*Template file*/ nullptr); - } else { - handle = ::CreateFileW( - mPath.get(), GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - /*Security attributes*/ nullptr, - // CREATE_ALWAYS is used since since have already checked the - // noOverwrite condition, and thus can overwrite safely. - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, - /*Template file*/ nullptr); - } - - if (handle == INVALID_HANDLE_VALUE) { - Fail("open"_ns, nullptr, ::GetLastError()); - return NS_ERROR_FAILURE; - } - - file = PR_ImportFile((PROsfd)handle); - if (!file) { - // |file| is closed by PR_ImportFile - Fail("ImportFile"_ns, nullptr, PR_GetOSError()); - return NS_ERROR_FAILURE; - } - -#else - // if we're dealing with a tmpFile, we need to write there. - if (!mTmpPath.IsVoid()) { - file = - PR_OpenFile(tmpPath.get(), PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, - PR_IRUSR | PR_IWUSR); - } else { - file = PR_OpenFile(path.get(), PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, - PR_IRUSR | PR_IWUSR); - } - - if (!file) { - Fail("open"_ns, nullptr, PR_GetOSError()); - return NS_ERROR_FAILURE; - } -#endif // defined(XP_WIN) - - int32_t bytesWrittenSuccess = - PR_Write(file, (void*)(mBuffer.get()), mBytes); - - if (bytesWrittenSuccess == -1) { - Fail("write"_ns, nullptr, PR_GetOSError()); - return NS_ERROR_FAILURE; - } - - // Apply any tmpPath renames. - if (!mTmpPath.IsVoid()) { - if (mBackupTo.IsVoid() && fileExists) { - // We need to delete the old file first, if it exists and we haven't - // already renamed it as a part of backing it up. -#if defined(XP_WIN) - if (::DeleteFileW(mPath.get()) == false) { - Fail("delete"_ns, nullptr, ::GetLastError()); - return NS_ERROR_FAILURE; - } -#else - if (PR_Delete(path.get()) == PR_FAILURE) { - Fail("delete"_ns, nullptr, PR_GetOSError()); - return NS_ERROR_FAILURE; - } -#endif // defined(XP_WIN) - } - -#if defined(XP_WIN) - if (::MoveFileW(mTmpPath.get(), mPath.get()) == false) { - Fail("rename"_ns, nullptr, ::GetLastError()); - return NS_ERROR_FAILURE; - } -#else - if (PR_Rename(tmpPath.get(), path.get()) == PR_FAILURE) { - Fail("rename"_ns, nullptr, PR_GetOSError()); - return NS_ERROR_FAILURE; - } -#endif // defined(XP_WIN) - } - - if (mFlush) { - if (PR_Sync(file) == PR_FAILURE) { - Fail("sync"_ns, nullptr, PR_GetOSError()); - return NS_ERROR_FAILURE; - } - } - - *aBytesWritten = bytesWrittenSuccess; - return NS_OK; - } - - protected: - nsresult AfterWriteAtomic(TimeStamp aDispatchDate, int32_t aBytesWritten) { - MOZ_ASSERT(!NS_IsMainThread()); - mResult->Init(aDispatchDate, TimeStamp::Now() - aDispatchDate, - aBytesWritten); - Succeed(mResult.forget()); - return NS_OK; - } - - const nsString mPath; - const UniquePtr<char[], JS::FreePolicy> mBuffer; - const int32_t mBytes; - const nsString mTmpPath; - const nsString mBackupTo; - const bool mFlush; - const bool mNoOverwrite; - - private: - RefPtr<Int32Result> mResult; -}; - -} // namespace - -// The OS.File service - -NS_IMPL_ISUPPORTS(NativeOSFileInternalsService, - nsINativeOSFileInternalsService); - -NS_IMETHODIMP -NativeOSFileInternalsService::Read(const nsAString& aPath, - JS::Handle<JS::Value> aOptions, - nsINativeOSFileSuccessCallback* aOnSuccess, - nsINativeOSFileErrorCallback* aOnError, - JSContext* cx) { - // Extract options - nsCString encoding; - uint64_t bytes = UINT64_MAX; - - if (aOptions.isObject()) { - dom::NativeOSFileReadOptions dict; - if (!dict.Init(cx, aOptions)) { - return NS_ERROR_INVALID_ARG; - } - - if (dict.mEncoding.WasPassed()) { - CopyUTF16toUTF8(dict.mEncoding.Value(), encoding); - } - - if (dict.mBytes.WasPassed() && !dict.mBytes.Value().IsNull()) { - bytes = dict.mBytes.Value().Value(); - } - } - - // Prepare the off main thread event and dispatch it - nsCOMPtr<nsINativeOSFileSuccessCallback> onSuccess(aOnSuccess); - nsMainThreadPtrHandle<nsINativeOSFileSuccessCallback> onSuccessHandle( - new nsMainThreadPtrHolder<nsINativeOSFileSuccessCallback>( - "nsINativeOSFileSuccessCallback", onSuccess)); - nsCOMPtr<nsINativeOSFileErrorCallback> onError(aOnError); - nsMainThreadPtrHandle<nsINativeOSFileErrorCallback> onErrorHandle( - new nsMainThreadPtrHolder<nsINativeOSFileErrorCallback>( - "nsINativeOSFileErrorCallback", onError)); - - RefPtr<AbstractDoEvent> event; - if (encoding.IsEmpty()) { - event = new DoReadToTypedArrayEvent(aPath, bytes, onSuccessHandle, - onErrorHandle); - } else { - event = new DoReadToStringEvent(aPath, encoding, bytes, onSuccessHandle, - onErrorHandle); - } - - nsresult rv; - nsCOMPtr<nsIEventTarget> target = - do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv); - - if (NS_FAILED(rv)) { - return rv; - } - return target->Dispatch(event, NS_DISPATCH_NORMAL); -} - -// Note: This method steals the contents of `aBuffer`. -NS_IMETHODIMP -NativeOSFileInternalsService::WriteAtomic( - const nsAString& aPath, JS::Handle<JS::Value> aBuffer, - JS::Handle<JS::Value> aOptions, nsINativeOSFileSuccessCallback* aOnSuccess, - nsINativeOSFileErrorCallback* aOnError, JSContext* cx) { - MOZ_ASSERT(NS_IsMainThread()); - // Extract typed-array/string into buffer. We also need to store the length - // of the buffer as that may be required if not provided in `aOptions`. - UniquePtr<char[], JS::FreePolicy> buffer; - int32_t bytes; - - // The incoming buffer must be an Object. - if (!aBuffer.isObject()) { - return NS_ERROR_INVALID_ARG; - } - - JS::Rooted<JSObject*> bufferObject(cx, nullptr); - if (!JS_ValueToObject(cx, aBuffer, &bufferObject)) { - return NS_ERROR_FAILURE; - } - if (!JS::IsArrayBufferObject(bufferObject.get())) { - return NS_ERROR_INVALID_ARG; - } - - { - // Throw for large ArrayBuffers to prevent truncation. - size_t len = JS::GetArrayBufferByteLength(bufferObject.get()); - if (len > INT32_MAX) { - return NS_ERROR_INVALID_ARG; - } - bytes = len; - } - buffer.reset( - static_cast<char*>(JS::StealArrayBufferContents(cx, bufferObject))); - - if (!buffer) { - return NS_ERROR_FAILURE; - } - - // Extract options. - dom::NativeOSFileWriteAtomicOptions dict; - - if (aOptions.isObject()) { - if (!dict.Init(cx, aOptions)) { - return NS_ERROR_INVALID_ARG; - } - } else { - // If an options object is not provided, initializing with a `null` - // value, which will give a set of defaults defined in the WebIDL binding. - if (!dict.Init(cx, JS::NullHandleValue)) { - return NS_ERROR_FAILURE; - } - } - - if (dict.mBytes.WasPassed() && !dict.mBytes.Value().IsNull()) { - // We need to check size and cast because NSPR and WebIDL have different - // types. - if (dict.mBytes.Value().Value() > PR_INT32_MAX) { - return NS_ERROR_INVALID_ARG; - } - bytes = (int32_t)(dict.mBytes.Value().Value()); - } - - // Prepare the off main thread event and dispatch it - nsCOMPtr<nsINativeOSFileSuccessCallback> onSuccess(aOnSuccess); - nsMainThreadPtrHandle<nsINativeOSFileSuccessCallback> onSuccessHandle( - new nsMainThreadPtrHolder<nsINativeOSFileSuccessCallback>( - "nsINativeOSFileSuccessCallback", onSuccess)); - nsCOMPtr<nsINativeOSFileErrorCallback> onError(aOnError); - nsMainThreadPtrHandle<nsINativeOSFileErrorCallback> onErrorHandle( - new nsMainThreadPtrHolder<nsINativeOSFileErrorCallback>( - "nsINativeOSFileErrorCallback", onError)); - - RefPtr<AbstractDoEvent> event = new DoWriteAtomicEvent( - aPath, std::move(buffer), bytes, dict.mTmpPath, dict.mBackupTo, - dict.mFlush, dict.mNoOverwrite, onSuccessHandle, onErrorHandle); - nsresult rv; - nsCOMPtr<nsIEventTarget> target = - do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv); - - if (NS_FAILED(rv)) { - return rv; - } - - return target->Dispatch(event, NS_DISPATCH_NORMAL); -} - -} // namespace mozilla
deleted file mode 100644 --- a/toolkit/components/osfile/NativeOSFileInternals.h +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_nativeosfileinternalservice_h__ -#define mozilla_nativeosfileinternalservice_h__ - -#include "nsINativeOSFileInternals.h" - -#include "nsISupports.h" - -namespace mozilla { - -class NativeOSFileInternalsService final - : public nsINativeOSFileInternalsService { - public: - NS_DECL_ISUPPORTS - NS_DECL_NSINATIVEOSFILEINTERNALSSERVICE - private: - ~NativeOSFileInternalsService() = default; - // Avoid accidental use of built-in operator= - void operator=(const NativeOSFileInternalsService& other) = delete; -}; - -} // namespace mozilla - -#endif // mozilla_finalizationwitnessservice_h__
deleted file mode 100644 --- a/toolkit/components/osfile/modules/moz.build +++ /dev/null @@ -1,29 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -EXTRA_JS_MODULES.osfile += [ - "osfile_async_front.jsm", - "osfile_async_worker.js", - "osfile_native.jsm", - "osfile_shared_allthreads.jsm", - "osfile_shared_front.js", - "ospath.jsm", - "ospath_unix.jsm", - "ospath_win.jsm", -] - -if CONFIG["OS_TARGET"] == "WINNT": - EXTRA_JS_MODULES.osfile += [ - "osfile_win_allthreads.jsm", - "osfile_win_back.js", - "osfile_win_front.js", - ] -else: - EXTRA_JS_MODULES.osfile += [ - "osfile_unix_allthreads.jsm", - "osfile_unix_back.js", - "osfile_unix_front.js", - ]
deleted file mode 100644 --- a/toolkit/components/osfile/modules/osfile_async_front.jsm +++ /dev/null @@ -1,1570 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Asynchronous front-end for OS.File. - * - * This front-end is meant to be imported from the main thread. In turn, - * it spawns one worker (perhaps more in the future) and delegates all - * disk I/O to this worker. - * - * Documentation note: most of the functions and methods in this module - * return promises. For clarity, we denote as follows a promise that may resolve - * with type |A| and some value |value| or reject with type |B| and some - * reason |reason| - * @resolves {A} value - * @rejects {B} reason - */ - -"use strict"; - -// Scheduler is exported for test-only usage. -var EXPORTED_SYMBOLS = ["OS", "Scheduler"]; - -var SharedAll = ChromeUtils.import( - "resource://gre/modules/osfile/osfile_shared_allthreads.jsm" -); -const { clearInterval, setInterval } = ChromeUtils.importESModule( - "resource://gre/modules/Timer.sys.mjs" -); - -// Boilerplate, to simplify the transition to require() -var LOG = SharedAll.LOG.bind(SharedAll, "Controller"); -var isTypedArray = SharedAll.isTypedArray; - -// The constructor for file errors. -var SysAll; -if (SharedAll.Constants.Win) { - SysAll = ChromeUtils.import( - "resource://gre/modules/osfile/osfile_win_allthreads.jsm" - ); -} else if (SharedAll.Constants.libc) { - SysAll = ChromeUtils.import( - "resource://gre/modules/osfile/osfile_unix_allthreads.jsm" - ); -} else { - throw new Error("I am neither under Windows nor under a Posix system"); -} -var OSError = SysAll.Error; -var Type = SysAll.Type; - -var Path = ChromeUtils.import("resource://gre/modules/osfile/ospath.jsm"); - -const lazy = {}; - -// The library of promises. -ChromeUtils.defineESModuleGetters(lazy, { - PromiseUtils: "resource://gre/modules/PromiseUtils.sys.mjs", -}); - -// The implementation of communications -const { BasePromiseWorker } = ChromeUtils.import( - "resource://gre/modules/PromiseWorker.jsm" -); -const { AsyncShutdown } = ChromeUtils.importESModule( - "resource://gre/modules/AsyncShutdown.sys.mjs" -); -var Native = ChromeUtils.import( - "resource://gre/modules/osfile/osfile_native.jsm" -); - -// It's possible for osfile.jsm to get imported before the profile is -// set up. In this case, some path constants aren't yet available. -// Here, we make them lazy loaders. - -function lazyPathGetter(constProp, dirKey) { - return function() { - let path; - try { - path = Services.dirsvc.get(dirKey, Ci.nsIFile).path; - delete SharedAll.Constants.Path[constProp]; - SharedAll.Constants.Path[constProp] = path; - } catch (ex) { - // Ignore errors if the value still isn't available. Hopefully - // the next access will return it. - } - - return path; - }; -} - -for (let [constProp, dirKey] of [ - ["localProfileDir", "ProfLD"], - ["profileDir", "ProfD"], - ["userApplicationDataDir", "UAppData"], - ["winAppDataDir", "AppData"], - ["winLocalAppDataDir", "LocalAppData"], - ["winStartMenuProgsDir", "Progs"], - ["tmpDir", "TmpD"], - ["homeDir", "Home"], - ["macUserLibDir", "ULibDir"], -]) { - if (constProp in SharedAll.Constants.Path) { - continue; - } - - LOG( - "Installing lazy getter for OS.Constants.Path." + - constProp + - " because it isn't defined and profile may not be loaded." - ); - Object.defineProperty(SharedAll.Constants.Path, constProp, { - get: lazyPathGetter(constProp, dirKey), - }); -} - -/** - * Return a shallow clone of the enumerable properties of an object. - */ -var clone = SharedAll.clone; - -/** - * Extract a shortened version of an object, fit for logging. - * - * This function returns a copy of the original object in which all - * long strings, Arrays, TypedArrays, ArrayBuffers are removed and - * replaced with placeholders. Use this function to sanitize objects - * if you wish to log them or to keep them in memory. - * - * @param {*} obj The obj to shorten. - * @return {*} array A shorter object, fit for logging. - */ -function summarizeObject(obj) { - if (!obj) { - return null; - } - if (typeof obj == "string") { - if (obj.length > 1024) { - return { "Long string": obj.length }; - } - return obj; - } - if (typeof obj == "object") { - if (Array.isArray(obj)) { - if (obj.length > 32) { - return { "Long array": obj.length }; - } - return obj.map(summarizeObject); - } - if ("byteLength" in obj) { - // Assume TypedArray or ArrayBuffer - return { "Binary Data": obj.byteLength }; - } - let result = {}; - for (let k of Object.keys(obj)) { - result[k] = summarizeObject(obj[k]); - } - return result; - } - return obj; -} - -var Scheduler = { - /** - * |true| once we have sent at least one message to the worker. - * This field is unaffected by resetting the worker. - */ - launched: false, - - /** - * |true| once shutdown has begun i.e. we should reject any - * message, including resets. - */ - shutdown: false, - - /** - * A promise resolved once all currently pending operations are complete. - * - * This promise is never rejected and the result is always undefined. - */ - queue: Promise.resolve(), - - /** - * A promise resolved once all currently pending `kill` operations - * are complete. - * - * This promise is never rejected and the result is always undefined. - */ - _killQueue: Promise.resolve(), - - /** - * Miscellaneous debugging information - */ - Debugging: { - /** - * The latest message sent and still waiting for a reply. - */ - latestSent: undefined, - - /** - * The latest reply received, or null if we are waiting for a reply. - */ - latestReceived: undefined, - - /** - * Number of messages sent to the worker. This includes the - * initial SET_DEBUG, if applicable. - */ - messagesSent: 0, - - /** - * Total number of messages ever queued, including the messages - * sent. - */ - messagesQueued: 0, - - /** - * Number of messages received from the worker. - */ - messagesReceived: 0, - }, - - /** - * A timer used to automatically shut down the worker after some time. - */ - resetTimer: null, - - /** - * A flag indicating whether we had some activities when waiting the - * timer and if it's not we can shut down the worker. - */ - hasRecentActivity: false, - - /** - * The worker to which to send requests. - * - * If the worker has never been created or has been reset, this is a - * fresh worker, initialized with osfile_async_worker.js. - * - * @type {PromiseWorker} - */ - get worker() { - if (!this._worker) { - // Either the worker has never been created or it has been - // reset. In either case, it is time to instantiate the worker. - this._worker = new BasePromiseWorker( - "resource://gre/modules/osfile/osfile_async_worker.js" - ); - this._worker.log = LOG; - this._worker.ExceptionHandlers["OS.File.Error"] = OSError.fromMsg; - - let delay = Services.prefs.getIntPref("osfile.reset_worker_delay", 0); - if (delay) { - this.resetTimer = setInterval(() => { - if (this.hasRecentActivity) { - this.hasRecentActivity = false; - return; - } - clearInterval(this.resetTimer); - Scheduler.kill({ reset: true, shutdown: false }); - }, delay); - } - } - return this._worker; - }, - - _worker: null, - - /** - * Restart the OS.File worker killer timer. - */ - restartTimer(arg) { - this.hasRecentActivity = true; - }, - - /** - * Shutdown OS.File. - * - * @param {*} options - * - {boolean} shutdown If |true|, reject any further request. Otherwise, - * further requests will resurrect the worker. - * - {boolean} reset If |true|, instruct the worker to shutdown if this - * would not cause leaks. Otherwise, assume that the worker will be shutdown - * through some other mean. - */ - kill({ shutdown, reset }) { - // Grab the kill queue to make sure that we - // cannot be interrupted by another call to `kill`. - let killQueue = this._killQueue; - - // Deactivate the queue, to ensure that no message is sent - // to an obsolete worker (we reactivate it in the `finally`). - // This needs to be done right now so that we maintain relative - // ordering with calls to post(), etc. - let deferred = lazy.PromiseUtils.defer(); - let savedQueue = this.queue; - this.queue = deferred.promise; - - return (this._killQueue = (async () => { - await killQueue; - // From this point, and until the end of the Task, we are the - // only call to `kill`, regardless of any `yield`. - - await savedQueue; - - try { - // Enter critical section: no yield in this block - // (we want to make sure that we remain the only - // request in the queue). - - if (!this.launched || this.shutdown || !this._worker) { - // Nothing to kill - this.shutdown = this.shutdown || shutdown; - this._worker = null; - return null; - } - - // Exit critical section - - let message = ["Meta_shutdown", [reset]]; - - Scheduler.latestReceived = []; - let stack = new Error().stack; - Scheduler.latestSent = [Date.now(), stack, ...message]; - - // Wait for result - let resources; - try { - resources = await this._worker.post(...message); - - Scheduler.latestReceived = [Date.now(), message]; - } catch (ex) { - LOG("Could not dispatch Meta_reset", ex); - // It's most likely a programmer error, but we'll assume that - // the worker has been shutdown, as it's less risky than the - // opposite stance. - resources = { - openedFiles: [], - openedDirectoryIterators: [], - killed: true, - }; - - Scheduler.latestReceived = [Date.now(), message, ex]; - } - - let { openedFiles, openedDirectoryIterators, killed } = resources; - if ( - !reset && - ((openedFiles && openedFiles.length) || - (openedDirectoryIterators && openedDirectoryIterators.length)) - ) { - // The worker still holds resources. Report them. - - let msg = ""; - if (openedFiles.length) { - msg += - "The following files are still open:\n" + openedFiles.join("\n"); - } - if (openedDirectoryIterators.length) { - msg += - "The following directory iterators are still open:\n" + - openedDirectoryIterators.join("\n"); - } - - LOG("WARNING: File descriptors leaks detected.\n" + msg); - } - - // Make sure that we do not leave an invalid |worker| around. - if (killed || shutdown) { - this._worker = null; - } - - this.shutdown = shutdown; - - return resources; - } finally { - // Resume accepting messages. If we have set |shutdown| to |true|, - // any pending/future request will be rejected. Otherwise, any - // pending/future request will spawn a new worker if necessary. - deferred.resolve(); - } - })()); - }, - - /** - * Push a task at the end of the queue. - * - * @param {function} code A function returning a Promise. - * This function will be executed once all the previously - * pushed tasks have completed. - * @return {Promise} A promise with the same behavior as - * the promise returned by |code|. - */ - push(code) { - let promise = this.queue.then(code); - // By definition, |this.queue| can never reject. - this.queue = promise.catch(() => undefined); - // Fork |promise| to ensure that uncaught errors are reported - return promise.then(); - }, - - /** - * Post a message to the worker thread. - * - * @param {string} method The name of the method to call. - * @param {...} args The arguments to pass to the method. These arguments - * must be clonable. - * The last argument by convention may be an object `options`, with some of - * the following fields: - * - {number|null} outSerializationDuration A parameter to be filled with - * duration of the `this.worker.post` method. - * @return {Promise} A promise conveying the result/error caused by - * calling |method| with arguments |args|. - */ - post: function post(method, args = undefined, closure = undefined) { - if (this.shutdown) { - LOG( - "OS.File is not available anymore. The following request has been rejected.", - method, - args - ); - return Promise.reject( - new Error("OS.File has been shut down. Rejecting post to " + method) - ); - } - let firstLaunch = !this.launched; - this.launched = true; - - if (firstLaunch && SharedAll.Config.DEBUG) { - // If we have delayed sending SET_DEBUG, do it now. - this.worker.post("SET_DEBUG", [true]); - Scheduler.Debugging.messagesSent++; - } - - Scheduler.Debugging.messagesQueued++; - return this.push(async () => { - if (this.shutdown) { - LOG( - "OS.File is not available anymore. The following request has been rejected.", - method, - args - ); - throw new Error( - "OS.File has been shut down. Rejecting request to " + method - ); - } - - // Update debugging information. As |args| may be quite - // expensive, we only keep a shortened version of it. - Scheduler.Debugging.latestReceived = null; - Scheduler.Debugging.latestSent = [ - Date.now(), - method, - summarizeObject(args), - ]; - - // Don't kill the worker just yet - Scheduler.restartTimer(); - - // The last object inside the args may be an options object. - let options = null; - if ( - args && - args.length >= 1 && - typeof args[args.length - 1] === "object" - ) { - options = args[args.length - 1]; - } - - let reply; - try { - try { - Scheduler.Debugging.messagesSent++; - Scheduler.Debugging.latestSent = Scheduler.Debugging.latestSent.slice( - 0, - 2 - ); - let serializationStartTimeMs = Date.now(); - reply = await this.worker.post(method, args, closure); - let serializationEndTimeMs = Date.now(); - Scheduler.Debugging.latestReceived = [ - Date.now(), - summarizeObject(reply), - ]; - - // There were no options for recording the serialization duration. - if (options && "outSerializationDuration" in options) { - // The difference might be negative for very fast operations, since Date.now() may not be monotonic. - let serializationDurationMs = Math.max( - 0, - serializationEndTimeMs - serializationStartTimeMs - ); - - if (typeof options.outSerializationDuration === "number") { - options.outSerializationDuration += serializationDurationMs; - } else { - options.outSerializationDuration = serializationDurationMs; - } - } - return reply; - } finally { - Scheduler.Debugging.messagesReceived++; - } - } catch (error) { - Scheduler.Debugging.latestReceived = [ - Date.now(), - error.message, - error.fileName, - error.lineNumber, - ]; - throw error; - } finally { - if (firstLaunch) { - Scheduler._updateTelemetry(); - } - Scheduler.restartTimer(); - } - }); - }, - - /** - * Post Telemetry statistics. - * - * This is only useful on first launch. - */ - _updateTelemetry() { - let worker = this.worker; - let workerTimeStamps = worker.workerTimeStamps; - if (!workerTimeStamps) { - // If the first call to OS.File results in an uncaught errors, - // the timestamps are absent. As this case is a developer error, - // let's not waste time attempting to extract telemetry from it. - return; - } - let HISTOGRAM_LAUNCH = Services.telemetry.getHistogramById( - "OSFILE_WORKER_LAUNCH_MS" - ); - HISTOGRAM_LAUNCH.add( - worker.workerTimeStamps.entered - worker.launchTimeStamp - ); - - let HISTOGRAM_READY = Services.telemetry.getHistogramById( - "OSFILE_WORKER_READY_MS" - ); - HISTOGRAM_READY.add( - worker.workerTimeStamps.loaded - worker.launchTimeStamp - ); - }, -}; - -const PREF_OSFILE_LOG = "toolkit.osfile.log"; -const PREF_OSFILE_LOG_REDIRECT = "toolkit.osfile.log.redirect"; - -/** - * Safely read a PREF_OSFILE_LOG preference. - * Returns a value read or, in case of an error, oldPref or false. - * - * @param bool oldPref - * An optional value that the DEBUG flag was set to previously. - */ -function readDebugPref(prefName, oldPref = false) { - // If neither pref nor oldPref were set, default it to false. - return Services.prefs.getBoolPref(prefName, oldPref); -} - -/** - * Listen to PREF_OSFILE_LOG changes and update gShouldLog flag - * appropriately. - */ -Services.prefs.addObserver(PREF_OSFILE_LOG, function prefObserver( - aSubject, - aTopic, - aData -) { - SharedAll.Config.DEBUG = readDebugPref( - PREF_OSFILE_LOG, - SharedAll.Config.DEBUG - ); - if (Scheduler.launched) { - // Don't start the worker just to set this preference. - Scheduler.post("SET_DEBUG", [SharedAll.Config.DEBUG]); - } -}); -SharedAll.Config.DEBUG = readDebugPref(PREF_OSFILE_LOG, false); - -Services.prefs.addObserver(PREF_OSFILE_LOG_REDIRECT, function prefObserver( - aSubject, - aTopic, - aData -) { - SharedAll.Config.TEST = readDebugPref( - PREF_OSFILE_LOG_REDIRECT, - OS.Shared.TEST - ); -}); -SharedAll.Config.TEST = readDebugPref(PREF_OSFILE_LOG_REDIRECT, false); - -/** - * If |true|, use the native implementaiton of OS.File methods - * whenever possible. Otherwise, force the use of the JS version. - */ -var nativeWheneverAvailable = true; -const PREF_OSFILE_NATIVE = "toolkit.osfile.native"; -Services.prefs.addObserver(PREF_OSFILE_NATIVE, function prefObserver( - aSubject, - aTopic, - aData -) { - nativeWheneverAvailable = readDebugPref( - PREF_OSFILE_NATIVE, - nativeWheneverAvailable - ); -}); - -// Update worker's DEBUG flag if it's true. -// Don't start the worker just for this, though. -if (SharedAll.Config.DEBUG && Scheduler.launched) { - Scheduler.post("SET_DEBUG", [true]); -} - -// Preference used to configure test shutdown observer. -const PREF_OSFILE_TEST_SHUTDOWN_OBSERVER = - "toolkit.osfile.test.shutdown.observer"; - -AsyncShutdown.xpcomWillShutdown.addBlocker( - "OS.File: flush pending requests, warn about unclosed files, shut down service.", - async function() { - // Give clients a last chance to enqueue requests. - await Barriers.shutdown.wait({ crashAfterMS: null }); - - // Wait until all requests are complete and kill the worker. - await Scheduler.kill({ reset: false, shutdown: true }); - }, - () => { - let details = Barriers.getDetails(); - details.clients = Barriers.shutdown.state; - return details; - } -); - -// Attaching an observer for PREF_OSFILE_TEST_SHUTDOWN_OBSERVER to enable or -// disable the test shutdown event observer. -// Note: By default the PREF_OSFILE_TEST_SHUTDOWN_OBSERVER is unset. -// Note: This is meant to be used for testing purposes only. -Services.prefs.addObserver( - PREF_OSFILE_TEST_SHUTDOWN_OBSERVER, - function prefObserver() { - // The temporary phase topic used to trigger the unclosed - // phase warning. - let TOPIC = Services.prefs.getCharPref( - PREF_OSFILE_TEST_SHUTDOWN_OBSERVER, - "" - ); - if (TOPIC) { - // Generate a phase, add a blocker. - // Note that this can work only if AsyncShutdown itself has been - // configured for testing by the testsuite. - let phase = AsyncShutdown._getPhase(TOPIC); - phase.addBlocker( - "(for testing purposes) OS.File: warn about unclosed files", - () => Scheduler.kill({ shutdown: false, reset: false }) - ); - } - } -); - -/** - * Representation of a file, with asynchronous methods. - * - * @param {*} fdmsg The _message_ representing the platform-specific file - * handle. - * - * @constructor - */ -var File = function File(fdmsg) { - // FIXME: At the moment, |File| does not close on finalize - // (see bug 777715) - this._fdmsg = fdmsg; - this._closeResult = null; - this._closed = null; -}; - -File.prototype = { - /** - * Close a file asynchronously. - * - * This method is idempotent. - * - * @return {promise} - * @resolves {null} - * @rejects {OS.File.Error} - */ - close: function close() { - if (this._fdmsg != null) { - let msg = this._fdmsg; - this._fdmsg = null; - return (this._closeResult = Scheduler.post( - "File_prototype_close", - [msg], - this - )); - } - return this._closeResult; - }, - - /** - * Fetch information about the file. - * - * @return {promise} - * @resolves {OS.File.Info} The latest information about the file. - * @rejects {OS.File.Error} - */ - stat: function stat() { - return Scheduler.post("File_prototype_stat", [this._fdmsg], this).then( - File.Info.fromMsg - ); - }, - - /** - * Write bytes from a buffer to this file. - * - * Note that, by default, this function may perform several I/O - * operations to ensure that the buffer is fully written. - * - * @param {Typed array | C pointer} buffer The buffer in which the - * the bytes are stored. The buffer must be large enough to - * accomodate |bytes| bytes. Using the buffer before the operation - * is complete is a BAD IDEA. - * @param {*=} options Optionally, an object that may contain the - * following fields: - * - {number} bytes The number of |bytes| to write from the buffer. If - * unspecified, this is |buffer.byteLength|. Note that |bytes| is required - * if |buffer| is a C pointer. - * - * @return {number} The number of bytes actually written. - */ - write: function write(buffer, options = {}) { - // If |buffer| is a typed array and there is no |bytes| options, - // we need to extract the |byteLength| now, as it will be lost - // by communication. - // Options might be a nullish value, so better check for that before using - // the |in| operator. - if (isTypedArray(buffer) && !(options && "bytes" in options)) { - // Preserve reference to option |outExecutionDuration|, |outSerializationDuration|, if it is passed. - options = clone(options, [ - "outExecutionDuration", - "outSerializationDuration", - ]); - options.bytes = buffer.byteLength; - } - return Scheduler.post( - "File_prototype_write", - [this._fdmsg, Type.void_t.in_ptr.toMsg(buffer), options], - buffer /* Ensure that |buffer| is not gc-ed*/ - ); - }, - - /** - * Read bytes from this file to a new buffer. - * - * @param {number=} bytes If unspecified, read all the remaining bytes from - * this file. If specified, read |bytes| bytes, or less if the file does not - * contain that many bytes. - * @param {JSON} options - * @return {promise} - * @resolves {Uint8Array} An array containing the bytes read. - */ - read: function read(nbytes, options = {}) { - let promise = Scheduler.post("File_prototype_read", [ - this._fdmsg, - nbytes, - options, - ]); - return promise.then(function onSuccess(data) { - return new Uint8Array(data.buffer, data.byteOffset, data.byteLength); - }); - }, - - /** - * Return the current position in the file, as bytes. - * - * @return {promise} - * @resolves {number} The current position in the file, - * as a number of bytes since the start of the file. - */ - getPosition: function getPosition() { - return Scheduler.post("File_prototype_getPosition", [this._fdmsg]); - }, - - /** - * Set the current position in the file, as bytes. - * - * @param {number} pos A number of bytes. - * @param {number} whence The reference position in the file, - * which may be either POS_START (from the start of the file), - * POS_END (from the end of the file) or POS_CUR (from the - * current position in the file). - * - * @return {promise} - */ - setPosition: function setPosition(pos, whence) { - return Scheduler.post("File_prototype_setPosition", [ - this._fdmsg, - pos, - whence, - ]); - }, - - /** - * Flushes the file's buffers and causes all buffered data - * to be written. - * Disk flushes are very expensive and therefore should be used carefully, - * sparingly and only in scenarios where it is vital that data survives - * system crashes. Even though the function will be executed off the - * main-thread, it might still affect the overall performance of any running - * application. - * - * @return {promise} - */ - flush: function flush() { - return Scheduler.post("File_prototype_flush", [this._fdmsg]); - }, - - /** - * Set the file's access permissions. This does nothing on Windows. - * - * This operation is likely to fail if applied to a file that was - * not created by the currently running program (more precisely, - * if it was created by a program running under a different OS-level - * user account). It may also fail, or silently do nothing, if the - * filesystem containing the file does not support access permissions. - * - * @param {*=} options Object specifying the requested permissions: - * - * - {number} unixMode The POSIX file mode to set on the file. If omitted, - * the POSIX file mode is reset to the default used by |OS.file.open|. If - * specified, the permissions will respect the process umask as if they - * had been specified as arguments of |OS.File.open|, unless the - * |unixHonorUmask| parameter tells otherwise. - * - {bool} unixHonorUmask If omitted or true, any |unixMode| value is - * modified by the process umask, as |OS.File.open| would have done. If - * false, the exact value of |unixMode| will be applied. - */ - setPermissions: function setPermissions(options = {}) { - return Scheduler.post("File_prototype_setPermissions", [ - this._fdmsg, - options, - ]); - }, -}; - -if (SharedAll.Constants.Sys.Name != "Android") { - /** - * Set the last access and modification date of the file. - * The time stamp resolution is 1 second at best, but might be worse - * depending on the platform. - * - * WARNING: This method is not implemented on Android/B2G. On Android/B2G, - * you should use File.setDates instead. - * - * @return {promise} - * @rejects {TypeError} - * @rejects {OS.File.Error} - */ - File.prototype.setDates = function(accessDate, modificationDate) { - return Scheduler.post( - "File_prototype_setDates", - [this._fdmsg, accessDate, modificationDate], - this - ); - }; -} - -/** - * Open a file asynchronously. - * - * @return {promise} - * @resolves {OS.File} - * @rejects {OS.Error} - */ -File.open = function open(path, mode, options) { - return Scheduler.post( - "open", - [Type.path.toMsg(path), mode, options], - path - ).then(function onSuccess(msg) { - return new File(msg); - }); -}; - -/** - * Creates and opens a file with a unique name. By default, generate a random HEX number and use it to create a unique new file name. - * - * @param {string} path The path to the file. - * @param {*=} options Additional options for file opening. This - * implementation interprets the following fields: - * - * - {number} humanReadable If |true|, create a new filename appending a decimal number. ie: filename-1.ext, filename-2.ext. - * If |false| use HEX numbers ie: filename-A65BC0.ext - * - {number} maxReadableNumber Used to limit the amount of tries after a failed - * file creation. Default is 20. - * - * @return {Object} contains A file object{file} and the path{path}. - * @throws {OS.File.Error} If the file could not be opened. - */ -File.openUnique = function openUnique(path, options) { - return Scheduler.post( - "openUnique", - [Type.path.toMsg(path), options], - path - ).then(function onSuccess(msg) { - return { - path: msg.path, - file: new File(msg.file), - }; - }); -}; - -/** - * Get the information on the file. - * - * @return {promise} - * @resolves {OS.File.Info} - * @rejects {OS.Error} - */ -File.stat = function stat(path, options) { - return Scheduler.post("stat", [Type.path.toMsg(path), options], path).then( - File.Info.fromMsg - ); -}; - -/** - * Set the last access and modification date of the file. - * The time stamp resolution is 1 second at best, but might be worse - * depending on the platform. - * - * @return {promise} - * @rejects {TypeError} - * @rejects {OS.File.Error} - */ -File.setDates = function setDates(path, accessDate, modificationDate) { - return Scheduler.post( - "setDates", - [Type.path.toMsg(path), accessDate, modificationDate], - this - ); -}; - -/** - * Set the file's access permissions. This does nothing on Windows. - * - * This operation is likely to fail if applied to a file that was - * not created by the currently running program (more precisely, - * if it was created by a program running under a different OS-level - * user account). It may also fail, or silently do nothing, if the - * filesystem containing the file does not support access permissions. - * - * @param {string} path The path to the file. - * @param {*=} options Object specifying the requested permissions: - * - * - {number} unixMode The POSIX file mode to set on the file. If omitted, - * the POSIX file mode is reset to the default used by |OS.file.open|. If - * specified, the permissions will respect the process umask as if they - * had been specified as arguments of |OS.File.open|, unless the - * |unixHonorUmask| parameter tells otherwise. - * - {bool} unixHonorUmask If omitted or true, any |unixMode| value is - * modified by the process umask, as |OS.File.open| would have done. If - * false, the exact value of |unixMode| will be applied. - */ -File.setPermissions = function setPermissions(path, options = {}) { - return Scheduler.post("setPermissions", [Type.path.toMsg(path), options]); -}; - -/** - * Fetch the current directory - * - * @return {promise} - * @resolves {string} The current directory, as a path usable with OS.Path - * @rejects {OS.Error} - */ -File.getCurrentDirectory = function getCurrentDirectory() { - return Scheduler.post("getCurrentDirectory").then(Type.path.fromMsg); -}; - -/** - * Copy a file to a destination. - * - * @param {string} sourcePath The platform-specific path at which - * the file may currently be found. - * @param {string} destPath The platform-specific path at which the - * file should be copied. - * @param {*=} options An object which may contain the following fields: - * - * @option {bool} noOverwrite - If true, this function will fail if - * a file already exists at |destPath|. Otherwise, if this file exists, - * it will be erased silently. - * - * @rejects {OS.File.Error} In case of any error. - * - * General note: The behavior of this function is defined only when - * it is called on a single file. If it is called on a directory, the - * behavior is undefined and may not be the same across all platforms. - * - * General note: The behavior of this function with respect to metadata - * is unspecified. Metadata may or may not be copied with the file. The - * behavior may not be the same across all platforms. - */ -File.copy = function copy(sourcePath, destPath, options) { - return Scheduler.post( - "copy", - [Type.path.toMsg(sourcePath), Type.path.toMsg(destPath), options], - [sourcePath, destPath] - ); -}; - -/** - * Move a file to a destination. - * - * @param {string} sourcePath The platform-specific path at which - * the file may currently be found. - * @param {string} destPath The platform-specific path at which the - * file should be moved. - * @param {*=} options An object which may contain the following fields: - * - * @option {bool} noOverwrite - If set, this function will fail if - * a file already exists at |destPath|. Otherwise, if this file exists, - * it will be erased silently. - * - * @returns {Promise} - * @rejects {OS.File.Error} In case of any error. - * - * General note: The behavior of this function is defined only when - * it is called on a single file. If it is called on a directory, the - * behavior is undefined and may not be the same across all platforms. - * - * General note: The behavior of this function with respect to metadata - * is unspecified. Metadata may or may not be moved with the file. The - * behavior may not be the same across all platforms. - */ -File.move = function move(sourcePath, destPath, options) { - return Scheduler.post( - "move", - [Type.path.toMsg(sourcePath), Type.path.toMsg(destPath), options], - [sourcePath, destPath] - ); -}; - -/** - * Create a symbolic link to a source. - * - * @param {string} sourcePath The platform-specific path to which - * the symbolic link should point. - * @param {string} destPath The platform-specific path at which the - * symbolic link should be created. - * - * @returns {Promise} - * @rejects {OS.File.Error} In case of any error. - */ -if (!SharedAll.Constants.Win) { - File.unixSymLink = function unixSymLink(sourcePath, destPath) { - return Scheduler.post( - "unixSymLink", - [Type.path.toMsg(sourcePath), Type.path.toMsg(destPath)], - [sourcePath, destPath] - ); - }; -} - -/** - * Remove an empty directory. - * - * @param {string} path The name of the directory to remove. - * @param {*=} options Additional options. - * - {bool} ignoreAbsent If |true|, do not fail if the - * directory does not exist yet. - */ -File.removeEmptyDir = function removeEmptyDir(path, options) { - return Scheduler.post( - "removeEmptyDir", - [Type.path.toMsg(path), options], - path - ); -}; - -/** - * Remove an existing file. - * - * @param {string} path The name of the file. - * @param {*=} options Additional options. - * - {bool} ignoreAbsent If |false|, throw an error if the file does - * not exist. |true| by default. - * - * @throws {OS.File.Error} In case of I/O error. - */ -File.remove = function remove(path, options) { - return Scheduler.post("remove", [Type.path.toMsg(path), options], path); -}; - -/** - * Create a directory and, optionally, its parent directories. - * - * @param {string} path The name of the directory. - * @param {*=} options Additional options. - * - * - {string} from If specified, the call to |makeDir| creates all the - * ancestors of |path| that are descendants of |from|. Note that |path| - * must be a descendant of |from|, and that |from| and its existing - * subdirectories present in |path| must be user-writeable. - * Example: - * makeDir(Path.join(profileDir, "foo", "bar"), { from: profileDir }); - * creates directories profileDir/foo, profileDir/foo/bar - * - {bool} ignoreExisting If |false|, throw an error if the directory - * already exists. |true| by default. Ignored if |from| is specified. - * - {number} unixMode Under Unix, if specified, a file creation mode, - * as per libc function |mkdir|. If unspecified, dirs are - * created with a default mode of 0700 (dir is private to - * the user, the user can read, write and execute). Ignored under Windows - * or if the file system does not support file creation modes. - * - {C pointer} winSecurity Under Windows, if specified, security - * attributes as per winapi function |CreateDirectory|. If - * unspecified, use the default security descriptor, inherited from - * the parent directory. Ignored under Unix or if the file system - * does not support security descriptors. - */ -File.makeDir = function makeDir(path, options) { - return Scheduler.post("makeDir", [Type.path.toMsg(path), options], path); -}; - -/** - * Return the contents of a file - * - * @param {string} path The path to the file. - * @param {number=} bytes Optionally, an upper bound to the number of bytes - * to read. DEPRECATED - please use options.bytes instead. - * @param {JSON} options Additional options. - * - {boolean} sequential A flag that triggers a population of the page cache - * with data from a file so that subsequent reads from that file would not - * block on disk I/O. If |true| or unspecified, inform the system that the - * contents of the file will be read in order. Otherwise, make no such - * assumption. |true| by default. - * - {number} bytes An upper bound to the number of bytes to read. - * - {string} compression If "lz4" and if the file is compressed using the lz4 - * compression algorithm, decompress the file contents on the fly. - * - * @resolves {Uint8Array} A buffer holding the bytes - * read from the file. - */ -File.read = function read(path, bytes, options = {}) { - if (typeof bytes == "object") { - // Passing |bytes| as an argument is deprecated. - // We should now be passing it as a field of |options|. - options = bytes || {}; - } else { - options = clone(options, [ - "outExecutionDuration", - "outSerializationDuration", - ]); - if (typeof bytes != "undefined") { - options.bytes = bytes; - } - } - - if (options.compression || !nativeWheneverAvailable) { - // We need to use the JS implementation. - let promise = Scheduler.post( - "read", - [Type.path.toMsg(path), bytes, options], - path - ); - return promise.then(function onSuccess(data) { - if (typeof data == "string") { - return data; - } - return new Uint8Array(data.buffer, data.byteOffset, data.byteLength); - }); - } - - // Otherwise, use the native implementation. - return Scheduler.push(() => Native.read(path, options)); -}; - -/** - * Find outs if a file exists. - * - * @param {string} path The path to the file. - * - * @return {bool} true if the file exists, false otherwise. - */ -File.exists = function exists(path) { - return Scheduler.post("exists", [Type.path.toMsg(path)], path); -}; - -/** - * Write a file, atomically. - * - * By opposition to a regular |write|, this operation ensures that, - * until the contents are fully written, the destination file is - * not modified. - * - * Limitation: In a few extreme cases (hardware failure during the - * write, user unplugging disk during the write, etc.), data may be - * corrupted. If your data is user-critical (e.g. preferences, - * application data, etc.), you may wish to consider adding options - * |tmpPath| and/or |flush| to reduce the likelihood of corruption, as - * detailed below. Note that no combination of options can be - * guaranteed to totally eliminate the risk of corruption. - * - * @param {string} path The path of the file to modify. - * @param {Typed Array | C pointer} buffer A buffer containing the bytes to write. - * @param {*=} options Optionally, an object determining the behavior - * of this function. This object may contain the following fields: - * - {number} bytes The number of bytes to write. If unspecified, - * |buffer.byteLength|. Required if |buffer| is a C pointer. - * - {string} tmpPath If |null| or unspecified, write all data directly - * to |path|. If specified, write all data to a temporary file called - * |tmpPath| and, once this write is complete, rename the file to - * replace |path|. Performing this additional operation is a little - * slower but also a little safer. - * - {bool} noOverwrite - If set, this function will fail if a file already - * exists at |path|. - * - {bool} flush - If |false| or unspecified, return immediately once the - * write is complete. If |true|, before writing, force the operating system - * to write its internal disk buffers to the disk. This is considerably slower - * (not just for the application but for the whole system) but also safer: - * if the system shuts down improperly (typically due to a kernel freeze - * or a power failure) or if the device is disconnected before the buffer - * is flushed, the file has more chances of not being corrupted. - * - {string} backupTo - If specified, backup the destination file as |backupTo|. - * Note that this function renames the destination file before overwriting it. - * If the process or the operating system freezes or crashes - * during the short window between these operations, - * the destination file will have been moved to its backup. - * - * @return {promise} - * @resolves {number} The number of bytes actually written. - */ -File.writeAtomic = function writeAtomic(path, buffer, options = {}) { - const useNativeImplementation = - nativeWheneverAvailable && - !options.compression && - !(isTypedArray(buffer) && "byteOffset" in buffer && buffer.byteOffset > 0); - // Copy |options| to avoid modifying the original object but preserve the - // reference to |outExecutionDuration|, |outSerializationDuration| option if it is passed. - options = clone(options, [ - "outExecutionDuration", - "outSerializationDuration", - ]); - // As options.tmpPath is a path, we need to encode it as |Type.path| message, but only - // if we are not using the native implementation. - if ("tmpPath" in options && !useNativeImplementation) { - options.tmpPath = Type.path.toMsg(options.tmpPath); - } - if (isTypedArray(buffer) && !("bytes" in options)) { - options.bytes = buffer.byteLength; - } - let refObj = {}; - let promise; - TelemetryStopwatch.start("OSFILE_WRITEATOMIC_JANK_MS", refObj); - if (useNativeImplementation) { - promise = Scheduler.push(() => Native.writeAtomic(path, buffer, options)); - } else { - promise = Scheduler.post( - "writeAtomic", - [Type.path.toMsg(path), Type.void_t.in_ptr.toMsg(buffer), options], - [options, buffer, path] - ); - } - TelemetryStopwatch.finish("OSFILE_WRITEATOMIC_JANK_MS", refObj); - return promise; -}; - -File.removeDir = function(path, options = {}) { - return Scheduler.post("removeDir", [Type.path.toMsg(path), options], path); -}; - -/** - * Information on a file, as returned by OS.File.stat or - * OS.File.prototype.stat - * - * @constructor - */ -File.Info = function Info(value) { - // Note that we can't just do this[k] = value[k] because our - // prototype defines getters for all of these fields. - for (let k in value) { - Object.defineProperty(this, k, { value: value[k] }); - } -}; -File.Info.prototype = SysAll.AbstractInfo.prototype; - -File.Info.fromMsg = function fromMsg(value) { - return new File.Info(value); -}; - -/** - * Get worker's current DEBUG flag. - * Note: This is used for testing purposes. - */ -File.GET_DEBUG = function GET_DEBUG() { - return Scheduler.post("GET_DEBUG"); -}; - -/** - * Iterate asynchronously through a directory - * - * @constructor - */ -var DirectoryIterator = function DirectoryIterator(path, options) { - /** - * Open the iterator on the worker thread - * - * @type {Promise} - * @resolves {*} A message accepted by the methods of DirectoryIterator - * in the worker thread - */ - this._itmsg = Scheduler.post( - "new_DirectoryIterator", - [Type.path.toMsg(path), options], - path - ); - this._isClosed = false; -}; -DirectoryIterator.prototype = { - [Symbol.asyncIterator]() { - return this; - }, - - _itmsg: null, - - /** - * Determine whether the directory exists. - * - * @resolves {boolean} - */ - async exists() { - if (this._isClosed) { - return Promise.resolve(false); - } - let iterator = await this._itmsg; - return Scheduler.post("DirectoryIterator_prototype_exists", [iterator]); - }, - /** - * Get the next entry in the directory. - * - * @return {Promise} - * @resolves By definition of the async iterator protocol, either - * `{value: {File.Entry}, done: false}` if there is an unvisited entry - * in the directory, or `{value: undefined, done: true}`, otherwise. - */ - async next() { - if (this._isClosed) { - return { value: undefined, done: true }; - } - return this._next(await this._itmsg); - }, - /** - * Get several entries at once. - * - * @param {number=} length If specified, the number of entries - * to return. If unspecified, return all remaining entries. - * @return {Promise} - * @resolves {Array} An array containing the |length| next entries. - */ - async nextBatch(size) { - if (this._isClosed) { - return []; - } - let iterator = await this._itmsg; - let array = await Scheduler.post("DirectoryIterator_prototype_nextBatch", [ - iterator, - size, - ]); - return array.map(DirectoryIterator.Entry.fromMsg); - }, - /** - * Apply a function to all elements of the directory sequentially. - * - * @param {Function} cb This function will be applied to all entries - * of the directory. It receives as arguments - * - the OS.File.Entry corresponding to the entry; - * - the index of the entry in the enumeration; - * - the iterator itself - return |iterator.close()| to stop the loop. - * - * If the callback returns a promise, iteration waits until the - * promise is resolved before proceeding. - * - * @return {Promise} A promise resolved once the loop has reached - * its end. - */ - async forEach(cb, options) { - if (this._isClosed) { - return undefined; - } - let position = 0; - let iterator = await this._itmsg; - while (true) { - if (this._isClosed) { - return undefined; - } - let { value, done } = await this._next(iterator); - if (done) { - return undefined; - } - await cb(value, position++, this); - } - }, - /** - * Auxiliary method: fetch the next item - * - * @resolves `{value: undefined, done: true}` If all entries have already - * been visited or the iterator has been closed. - */ - async _next(iterator) { - if (this._isClosed) { - return { value: undefined, done: true }; - } - let { - value, - done, - } = await Scheduler.post("DirectoryIterator_prototype_next", [iterator]); - if (done) { - this.close(); - return { value: undefined, done: true }; - } - return { value: DirectoryIterator.Entry.fromMsg(value), done: false }; - }, - /** - * Close the iterator - */ - async close() { - if (this._isClosed) { - return undefined; - } - this._isClosed = true; - let iterator = this._itmsg; - this._itmsg = null; - return Scheduler.post("DirectoryIterator_prototype_close", [iterator]); - }, -}; - -DirectoryIterator.Entry = function Entry(value) { - return value; -}; -DirectoryIterator.Entry.prototype = Object.create( - SysAll.AbstractEntry.prototype -); - -DirectoryIterator.Entry.fromMsg = function fromMsg(value) { - return new DirectoryIterator.Entry(value); -}; - -File.resetWorker = function() { - return (async function() { - let resources = await Scheduler.kill({ shutdown: false, reset: true }); - if (resources && !resources.killed) { - throw new Error( - "Could not reset worker, this would leak file descriptors: " + - JSON.stringify(resources) - ); - } - })(); -}; - -// Constants -File.POS_START = SysAll.POS_START; -File.POS_CURRENT = SysAll.POS_CURRENT; -File.POS_END = SysAll.POS_END; - -// Exports -File.Error = OSError; -File.DirectoryIterator = DirectoryIterator; - -var OS = {}; -OS.File = File; -OS.Constants = SharedAll.Constants; -OS.Shared = { - LOG: SharedAll.LOG, - Type: SysAll.Type, - get DEBUG() { - return SharedAll.Config.DEBUG; - }, - set DEBUG(x) { - SharedAll.Config.DEBUG = x; - }, -}; -Object.freeze(OS.Shared); -OS.Path = Path; - -// Returns a resolved promise when all the queued operation have been completed. -Object.defineProperty(OS.File, "queue", { - get() { - return Scheduler.queue; - }, -}); - -// `true` if this is a content process, `false` otherwise. -// It would be nicer to go through `Services.appinfo`, but some tests need to be -// able to replace that field with a custom implementation before it is first -// called. -const isContent = - // eslint-disable-next-line mozilla/use-services - Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).processType == - Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT; - -/** - * Shutdown barriers, to let clients register to be informed during shutdown. - */ -var Barriers = { - shutdown: new AsyncShutdown.Barrier( - "OS.File: Waiting for clients before full shutdown" - ), - /** - * Return the shutdown state of OS.File - */ - getDetails() { - let result = { - launched: Scheduler.launched, - shutdown: Scheduler.shutdown, - worker: !!Scheduler._worker, - pendingReset: !!Scheduler.resetTimer, - latestSent: Scheduler.Debugging.latestSent, - latestReceived: Scheduler.Debugging.latestReceived, - messagesSent: Scheduler.Debugging.messagesSent, - messagesReceived: Scheduler.Debugging.messagesReceived, - messagesQueued: Scheduler.Debugging.messagesQueued, - DEBUG: SharedAll.Config.DEBUG, - }; - // Convert dates to strings for better readability - for (let key of ["latestSent", "latestReceived"]) { - if (result[key] && typeof result[key][0] == "number") { - result[key][0] = Date(result[key][0]); - } - } - return result; - }, -}; - -function setupShutdown(phaseName) { - Barriers[phaseName] = new AsyncShutdown.Barrier( - `OS.File: Waiting for clients before ${phaseName}` - ); - File[phaseName] = Barriers[phaseName].client; - - // Auto-flush OS.File during `phaseName`. This ensures that any I/O - // that has been queued *before* `phaseName` is properly completed. - // To ensure that I/O queued *during* `phaseName` change is completed, - // clients should register using AsyncShutdown.addBlocker. - AsyncShutdown[phaseName].addBlocker( - `OS.File: flush I/O queued before ${phaseName}`, - async function() { - // Give clients a last chance to enqueue requests. - await Barriers[phaseName].wait({ crashAfterMS: null }); - - // Wait until all currently enqueued requests are completed. - await Scheduler.queue; - }, - () => { - let details = Barriers.getDetails(); - details.clients = Barriers[phaseName].state; - return details; - } - ); -} - -// profile-before-change only exists in the parent, and OS.File should -// not be used in the child process anyways. -if (!isContent) { - setupShutdown("profileBeforeChange"); -} -File.shutdown = Barriers.shutdown.client;
deleted file mode 100644 --- a/toolkit/components/osfile/modules/osfile_async_worker.js +++ /dev/null @@ -1,450 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* eslint-env worker */ - -if (this.Components) { - throw new Error("This worker can only be loaded from a worker thread"); -} - -// Worker thread for osfile asynchronous front-end - -(function(exports) { - "use strict"; - - // Timestamps, for use in Telemetry. - // The object is set to |null| once it has been sent - // to the main thread. - let timeStamps = { - entered: Date.now(), - loaded: null, - }; - - // NOTE: osfile.jsm imports require.js - /* import-globals-from /toolkit/components/workerloader/require.js */ - /* import-globals-from /toolkit/components/osfile/osfile.jsm */ - importScripts("resource://gre/modules/osfile.jsm"); - - let PromiseWorker = require("resource://gre/modules/workers/PromiseWorker.js"); - let SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm"); - let LOG = SharedAll.LOG.bind(SharedAll, "Agent"); - - let worker = new PromiseWorker.AbstractWorker(); - worker.dispatch = function(method, args = []) { - let startTime = performance.now(); - try { - return Agent[method](...args); - } finally { - let text = method; - if (args.length && args[0] instanceof Object && args[0].string) { - // Including the path in the marker text here means it will be part of - // profiles. It's fine to include personally identifiable information - // in profiles, because when a profile is captured only the user will - // see it, and before uploading it a sanitization step will be offered. - // The 'OS.File' name will help the profiler know that these markers - // should be sanitized. - text += " — " + args[0].string; - } - ChromeUtils.addProfilerMarker("OS.File", startTime, text); - } - }; - worker.log = LOG; - worker.postMessage = function(message, ...transfers) { - if (timeStamps) { - message.timeStamps = timeStamps; - timeStamps = null; - } - self.postMessage(message, ...transfers); - }; - worker.close = function() { - self.close(); - }; - let Meta = PromiseWorker.Meta; - - self.addEventListener("message", msg => worker.handleMessage(msg)); - self.addEventListener("unhandledrejection", function(error) { - throw error.reason; - }); - - /** - * A data structure used to track opened resources - */ - let ResourceTracker = function ResourceTracker() { - // A number used to generate ids - this._idgen = 0; - // A map from id to resource - this._map = new Map(); - }; - ResourceTracker.prototype = { - /** - * Get a resource from its unique identifier. - */ - get(id) { - let result = this._map.get(id); - if (result == null) { - return result; - } - return result.resource; - }, - /** - * Remove a resource from its unique identifier. - */ - remove(id) { - if (!this._map.has(id)) { - throw new Error("Cannot find resource id " + id); - } - this._map.delete(id); - }, - /** - * Add a resource, return a new unique identifier - * - * @param {*} resource A resource. - * @param {*=} info Optional information. For debugging purposes. - * - * @return {*} A unique identifier. For the moment, this is a number, - * but this might not remain the case forever. - */ - add(resource, info) { - let id = this._idgen++; - this._map.set(id, { resource, info }); - return id; - }, - /** - * Return a list of all open resources i.e. the ones still present in - * ResourceTracker's _map. - */ - listOpenedResources: function listOpenedResources() { - return Array.from(this._map, ([id, resource]) => resource.info.path); - }, - }; - - /** - * A map of unique identifiers to opened files. - */ - let OpenedFiles = new ResourceTracker(); - - /** - * Execute a function in the context of a given file. - * - * @param {*} id A unique identifier, as used by |OpenFiles|. - * @param {Function} f A function to call. - * @param {boolean} ignoreAbsent If |true|, the error is ignored. Otherwise, the error causes an exception. - * @return The return value of |f()| - * - * This function attempts to get the file matching |id|. If - * the file exists, it executes |f| within the |this| set - * to the corresponding file. Otherwise, it throws an error. - */ - let withFile = function withFile(id, f, ignoreAbsent) { - let file = OpenedFiles.get(id); - if (file == null) { - if (!ignoreAbsent) { - throw OS.File.Error.closed("accessing file"); - } - return undefined; - } - return f.call(file); - }; - - let OpenedDirectoryIterators = new ResourceTracker(); - let withDir = function withDir(fd, f, ignoreAbsent) { - let file = OpenedDirectoryIterators.get(fd); - if (file == null) { - if (!ignoreAbsent) { - throw OS.File.Error.closed("accessing directory"); - } - return undefined; - } - if (!(file instanceof File.DirectoryIterator)) { - throw new Error( - "file is not a directory iterator " + - Object.getPrototypeOf(file).toSource() - ); - } - return f.call(file); - }; - - let Type = exports.OS.Shared.Type; - - let File = exports.OS.File; - - /** - * The agent. - * - * It is in charge of performing method-specific deserialization - * of messages, calling the function/method of OS.File and serializing - * back the results. - */ - let Agent = { - // Update worker's OS.Shared.DEBUG flag message from controller. - SET_DEBUG(aDEBUG) { - SharedAll.Config.DEBUG = aDEBUG; - }, - // Return worker's current OS.Shared.DEBUG value to controller. - // Note: This is used for testing purposes. - GET_DEBUG() { - return SharedAll.Config.DEBUG; - }, - /** - * Execute shutdown sequence, returning data on leaked file descriptors. - * - * @param {bool} If |true|, kill the worker if this would not cause - * leaks. - */ - Meta_shutdown(kill) { - let result = { - openedFiles: OpenedFiles.listOpenedResources(), - openedDirectoryIterators: OpenedDirectoryIterators.listOpenedResources(), - killed: false, // Placeholder - }; - - // Is it safe to kill the worker? - let safe = - !result.openedFiles.length && !result.openedDirectoryIterators.length; - result.killed = safe && kill; - - return new Meta(result, { shutdown: result.killed }); - }, - // Functions of OS.File - stat: function stat(path, options) { - return exports.OS.File.Info.toMsg( - exports.OS.File.stat(Type.path.fromMsg(path), options) - ); - }, - setPermissions: function setPermissions(path, options = {}) { - return exports.OS.File.setPermissions(Type.path.fromMsg(path), options); - }, - setDates: function setDates(path, accessDate, modificationDate) { - return exports.OS.File.setDates( - Type.path.fromMsg(path), - accessDate, - modificationDate - ); - }, - getCurrentDirectory: function getCurrentDirectory() { - return exports.OS.Shared.Type.path.toMsg(File.getCurrentDirectory()); - }, - copy: function copy(sourcePath, destPath, options) { - return File.copy( - Type.path.fromMsg(sourcePath), - Type.path.fromMsg(destPath), - options - ); - }, - move: function move(sourcePath, destPath, options) { - return File.move( - Type.path.fromMsg(sourcePath), - Type.path.fromMsg(destPath), - options - ); - }, - makeDir: function makeDir(path, options) { - return File.makeDir(Type.path.fromMsg(path), options); - }, - removeEmptyDir: function removeEmptyDir(path, options) { - return File.removeEmptyDir(Type.path.fromMsg(path), options); - }, - remove: function remove(path, options) { - return File.remove(Type.path.fromMsg(path), options); - }, - open: function open(path, mode, options) { - let filePath = Type.path.fromMsg(path); - let file = File.open(filePath, mode, options); - return OpenedFiles.add(file, { - // Adding path information to keep track of opened files - // to report leaks when debugging. - path: filePath, - }); - }, - openUnique: function openUnique(path, options) { - let filePath = Type.path.fromMsg(path); - let openedFile = OS.Shared.AbstractFile.openUnique(filePath, options); - let resourceId = OpenedFiles.add(openedFile.file, { - // Adding path information to keep track of opened files - // to report leaks when debugging. - path: openedFile.path, - }); - - return { - path: openedFile.path, - file: resourceId, - }; - }, - read: function read(path, bytes, options) { - let data = File.read(Type.path.fromMsg(path), bytes, options); - if (typeof data == "string") { - return data; - } - return new Meta( - { - buffer: data.buffer, - byteOffset: data.byteOffset, - byteLength: data.byteLength, - }, - { - transfers: [data.buffer], - } - ); - }, - exists: function exists(path) { - return File.exists(Type.path.fromMsg(path)); - }, - writeAtomic: function writeAtomic(path, buffer, options) { - if (options.tmpPath) { - options.tmpPath = Type.path.fromMsg(options.tmpPath); - } - return File.writeAtomic( - Type.path.fromMsg(path), - Type.voidptr_t.fromMsg(buffer), - options - ); - }, - removeDir(path, options) { - return File.removeDir(Type.path.fromMsg(path), options); - }, - new_DirectoryIterator: function new_DirectoryIterator(path, options) { - let directoryPath = Type.path.fromMsg(path); - let iterator = new File.DirectoryIterator(directoryPath, options); - return OpenedDirectoryIterators.add(iterator, { - // Adding path information to keep track of opened directory - // iterators to report leaks when debugging. - path: directoryPath, - }); - }, - // Methods of OS.File - File_prototype_close: function close(fd) { - return withFile(fd, function do_close() { - try { - return this.close(); - } finally { - OpenedFiles.remove(fd); - } - }); - }, - File_prototype_stat: function stat(fd) { - return withFile(fd, function do_stat() { - return exports.OS.File.Info.toMsg(this.stat()); - }); - }, - File_prototype_setPermissions: function setPermissions(fd, options = {}) { - return withFile(fd, function do_setPermissions() { - return this.setPermissions(options); - }); - }, - File_prototype_setDates: function setDates( - fd, - accessTime, - modificationTime - ) { - return withFile(fd, function do_setDates() { - return this.setDates(accessTime, modificationTime); - }); - }, - File_prototype_read: function read(fd, nbytes, options) { - return withFile(fd, function do_read() { - let data = this.read(nbytes, options); - return new Meta( - { - buffer: data.buffer, - byteOffset: data.byteOffset, - byteLength: data.byteLength, - }, - { - transfers: [data.buffer], - } - ); - }); - }, - File_prototype_readTo: function readTo(fd, buffer, options) { - return withFile(fd, function do_readTo() { - return this.readTo( - exports.OS.Shared.Type.voidptr_t.fromMsg(buffer), - options - ); - }); - }, - File_prototype_write: function write(fd, buffer, options) { - return withFile(fd, function do_write() { - return this.write( - exports.OS.Shared.Type.voidptr_t.fromMsg(buffer), - options - ); - }); - }, - File_prototype_setPosition: function setPosition(fd, pos, whence) { - return withFile(fd, function do_setPosition() { - return this.setPosition(pos, whence); - }); - }, - File_prototype_getPosition: function getPosition(fd) { - return withFile(fd, function do_getPosition() { - return this.getPosition(); - }); - }, - File_prototype_flush: function flush(fd) { - return withFile(fd, function do_flush() { - return this.flush(); - }); - }, - // Methods of OS.File.DirectoryIterator - DirectoryIterator_prototype_next: function next(dir) { - return withDir( - dir, - function do_next() { - let { value, done } = this.next(); - if (done) { - OpenedDirectoryIterators.remove(dir); - return { value: undefined, done: true }; - } - return { - value: File.DirectoryIterator.Entry.toMsg(value), - done: false, - }; - }, - false - ); - }, - DirectoryIterator_prototype_nextBatch: function nextBatch(dir, size) { - return withDir( - dir, - function do_nextBatch() { - let result; - try { - result = this.nextBatch(size); - } catch (x) { - OpenedDirectoryIterators.remove(dir); - throw x; - } - return result.map(File.DirectoryIterator.Entry.toMsg); - }, - false - ); - }, - DirectoryIterator_prototype_close: function close(dir) { - return withDir( - dir, - function do_close() { - this.close(); - OpenedDirectoryIterators.remove(dir); - }, - true - ); // ignore error to support double-closing |DirectoryIterator| - }, - DirectoryIterator_prototype_exists: function exists(dir) { - return withDir(dir, function do_exists() { - return this.exists(); - }); - }, - }; - if (!SharedAll.Constants.Win) { - Agent.unixSymLink = function unixSymLink(sourcePath, destPath) { - return File.unixSymLink( - Type.path.fromMsg(sourcePath), - Type.path.fromMsg(destPath) - ); - }; - } - - timeStamps.loaded = Date.now(); -})(this);
deleted file mode 100644 --- a/toolkit/components/osfile/modules/osfile_native.jsm +++ /dev/null @@ -1,126 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Native (xpcom) implementation of key OS.File functions - */ - -"use strict"; - -var EXPORTED_SYMBOLS = ["read", "writeAtomic"]; - -var { Constants } = ChromeUtils.import( - "resource://gre/modules/osfile/osfile_shared_allthreads.jsm" -); - -var SysAll; -if (Constants.Win) { - SysAll = ChromeUtils.import( - "resource://gre/modules/osfile/osfile_win_allthreads.jsm" - ); -} else if (Constants.libc) { - SysAll = ChromeUtils.import( - "resource://gre/modules/osfile/osfile_unix_allthreads.jsm" - ); -} else { - throw new Error("I am neither under Windows nor under a Posix system"); -} -var { XPCOMUtils } = ChromeUtils.importESModule( - "resource://gre/modules/XPCOMUtils.sys.mjs" -); - -const lazy = {}; - -/** - * The native service holding the implementation of the functions. - */ -XPCOMUtils.defineLazyServiceGetter( - lazy, - "Internals", - "@mozilla.org/toolkit/osfile/native-internals;1", - "nsINativeOSFileInternalsService" -); - -/** - * Native implementation of OS.File.read - * - * This implementation does not handle option |compression|. - */ -var read = function(path, options = {}) { - // Sanity check on types of options - if ("encoding" in options && typeof options.encoding != "string") { - return Promise.reject(new TypeError("Invalid type for option encoding")); - } - if ("compression" in options && typeof options.compression != "string") { - return Promise.reject(new TypeError("Invalid type for option compression")); - } - if ("bytes" in options && typeof options.bytes != "number") { - return Promise.reject(new TypeError("Invalid type for option bytes")); - } - - return new Promise((resolve, reject) => { - lazy.Internals.read( - path, - options, - function onSuccess(success) { - success.QueryInterface(Ci.nsINativeOSFileResult); - if ("outExecutionDuration" in options) { - options.outExecutionDuration = - success.executionDurationMS + (options.outExecutionDuration || 0); - } - resolve(success.result); - }, - function onError(operation, oserror) { - reject(new SysAll.Error(operation, oserror, path)); - } - ); - }); -}; - -/** - * Native implementation of OS.File.writeAtomic. - * This should not be called when |buffer| is a view with some non-zero byte offset. - * Does not handle option |compression|. - */ -var writeAtomic = function(path, buffer, options = {}) { - // Sanity check on types of options - we check only the encoding, since - // the others are checked inside Internals.writeAtomic. - if ("encoding" in options && typeof options.encoding !== "string") { - return Promise.reject(new TypeError("Invalid type for option encoding")); - } - - if (typeof buffer == "string") { - // Normalize buffer to a C buffer by encoding it - buffer = new TextEncoder().encode(buffer); - } - - if (ArrayBuffer.isView(buffer)) { - // We need to throw an error if it's a buffer with some byte offset. - if ("byteOffset" in buffer && buffer.byteOffset > 0) { - return Promise.reject( - new Error("Invalid non-zero value of Typed Array byte offset") - ); - } - buffer = buffer.buffer; - } - - return new Promise((resolve, reject) => { - lazy.Internals.writeAtomic( - path, - buffer, - options, - function onSuccess(success) { - success.QueryInterface(Ci.nsINativeOSFileResult); - if ("outExecutionDuration" in options) { - options.outExecutionDuration = - success.executionDurationMS + (options.outExecutionDuration || 0); - } - resolve(success.result); - }, - function onError(operation, oserror) { - reject(new SysAll.Error(operation, oserror, path)); - } - ); - }); -};
deleted file mode 100644 --- a/toolkit/components/osfile/modules/osfile_shared_allthreads.jsm +++ /dev/null @@ -1,1371 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -/** - * OS.File utilities used by all threads. - * - * This module defines: - * - logging; - * - the base constants; - * - base types and primitives for declaring new types; - * - primitives for importing C functions; - * - primitives for dealing with integers, pointers, typed arrays; - * - the base class OSError; - * - a few additional utilities. - */ - -/* eslint-env worker */ - -// Boilerplate used to be able to import this module both from the main -// thread and from worker threads. - -/** - * A constructor for messages that require transfers instead of copies. - * - * See BasePromiseWorker.Meta. - * - * @constructor - */ -var Meta; -if (typeof Components != "undefined") { - // Global definition of |exports|, to keep everybody happy. - // In non-main thread, |exports| is provided by the module - // loader. - // eslint-disable-next-line mozilla/reject-global-this - this.exports = {}; - Meta = ChromeUtils.import("resource://gre/modules/PromiseWorker.jsm") - .BasePromiseWorker.Meta; -} else { - /* import-globals-from /toolkit/components/workerloader/require.js */ - importScripts("resource://gre/modules/workers/require.js"); - Meta = require("resource://gre/modules/workers/PromiseWorker.js").Meta; -} - -var EXPORTED_SYMBOLS = [ - "LOG", - "clone", - "Config", - "Constants", - "Type", - "HollowStructure", - "OSError", - "Library", - "declareFFI", - "declareLazy", - "declareLazyFFI", - "normalizeBufferArgs", - "projectValue", - "isArrayBuffer", - "isTypedArray", - "defineLazyGetter", - "OS", // Warning: this exported symbol will disappear -]; - -// //////////////////// Configuration of OS.File - -var Config = { - /** - * If |true|, calls to |LOG| are shown. Otherwise, they are hidden. - * - * This configuration option is controlled by preference "toolkit.osfile.log". - */ - DEBUG: false, - - /** - * TEST - */ - TEST: false, -}; -exports.Config = Config; - -// //////////////////// OS Constants - -if (typeof Components != "undefined") { - // On the main thread, OS.Constants is defined by a xpcom - // component. On other threads, it is available automatically - /* global OS */ - var { ctypes } = ChromeUtils.importESModule( - "resource://gre/modules/ctypes.sys.mjs" - ); - Cc["@mozilla.org/net/osfileconstantsservice;1"] - .getService(Ci.nsIOSFileConstantsService) - .init(); -} else { - ctypes = self.ctypes; -} - -exports.Constants = OS.Constants; - -// /////////////////// Utilities - -// Define a lazy getter for a property -var defineLazyGetter = function defineLazyGetter(object, name, getter) { - Object.defineProperty(object, name, { - configurable: true, - get: function lazy() { - delete this[name]; - let value = getter.call(this); - Object.defineProperty(object, name, { - value, - }); - return value; - }, - }); -}; -exports.defineLazyGetter = defineLazyGetter; - -// /////////////////// Logging - -/** - * The default implementation of the logger. - * - * The choice of logger can be overridden with Config.TEST. - */ -var gLogger; -// eslint-disable-next-line no-undef -if (typeof window != "undefined" && window.console && console.log) { - gLogger = console.log.bind(console, "OS"); -} else { - gLogger = function(...args) { - dump("OS " + args.join(" ") + "\n"); - }; -} - -/** - * Attempt to stringify an argument into something useful for - * debugging purposes, by using |.toString()| or |JSON.stringify| - * if available. - * - * @param {*} arg An argument to be stringified if possible. - * @return {string} A stringified version of |arg|. - */ -var stringifyArg = function stringifyArg(arg) { - if (typeof arg === "string") { - return arg; - } - if (arg && typeof arg === "object") { - let argToString = "" + arg; - - /** - * The only way to detect whether this object has a non-default - * implementation of |toString| is to check whether it returns - * '[object Object]'. Unfortunately, we cannot simply compare |arg.toString| - * and |Object.prototype.toString| as |arg| typically comes from another - * compartment. - */ - if (argToString === "[object Object]") { - return JSON.stringify(arg, function(key, value) { - if (isTypedArray(value)) { - return ( - "[" + - value.constructor.name + - " " + - value.byteOffset + - " " + - value.byteLength + - "]" - ); - } - if (isArrayBuffer(arg)) { - return "[" + value.constructor.name + " " + value.byteLength + "]"; - } - return value; - }); - } - return argToString; - } - return arg; -}; - -var LOG = function(...args) { - if (!Config.DEBUG) { - // If logging is deactivated, don't log - return; - } - - let logFunc = gLogger; - if (Config.TEST && typeof Components != "undefined") { - // If _TESTING_LOGGING is set, and if we are on the main thread, - // redirect logs to Services.console, for testing purposes - logFunc = function logFunc(...args) { - let message = ["TEST", "OS"].concat(args).join(" "); - Services.console.logStringMessage(message + "\n"); - }; - } - logFunc.apply(null, args.map(stringifyArg)); -}; - -exports.LOG = LOG; - -/** - * Return a shallow clone of the enumerable properties of an object. - * - * Utility used whenever normalizing options requires making (shallow) - * changes to an option object. The copy ensures that we do not modify - * a client-provided object by accident. - * - * Note: to reference and not copy specific fields, provide an optional - * |refs| argument containing their names. - * - * @param {JSON} object Options to be cloned. - * @param {Array} refs An optional array of field names to be passed by - * reference instead of copying. - */ -var clone = function(object, refs = []) { - let result = {}; - // Make a reference between result[key] and object[key]. - let refer = function refer(result, key, object) { - Object.defineProperty(result, key, { - enumerable: true, - get() { - return object[key]; - }, - set(value) { - object[key] = value; - }, - }); - }; - for (let k in object) { - if (!refs.includes(k)) { - result[k] = object[k]; - } else { - refer(result, k, object); - } - } - return result; -}; - -exports.clone = clone; - -// /////////////////// Abstractions above js-ctypes - -/** - * Abstraction above js-ctypes types. - * - * Use values of this type to register FFI functions. In addition to the - * usual features of js-ctypes, values of this type perform the necessary - * transformations to ensure that C errors are handled nicely, to connect - * resources with their finalizer, etc. - * - * @param {string} name The name of the type. Must be unique. - * @param {CType} implementation The js-ctypes implementation of the type. - * - * @constructor - */ -function Type(name, implementation) { - if (!(typeof name == "string")) { - throw new TypeError("Type expects as first argument a name, got: " + name); - } - if (!(implementation instanceof ctypes.CType)) { - throw new TypeError( - "Type expects as second argument a ctypes.CType" + - ", got: " + - implementation - ); - } - Object.defineProperty(this, "name", { value: name }); - Object.defineProperty(this, "implementation", { value: implementation }); -} -Type.prototype = { - /** - * Serialize a value of |this| |Type| into a format that can - * be transmitted as a message (not necessarily a string). - * - * In the default implementation, the method returns the - * value unchanged. - */ - toMsg: function default_toMsg(value) { - return value; - }, - /** - * Deserialize a message to a value of |this| |Type|. - * - * In the default implementation, the method returns the - * message unchanged. - */ - fromMsg: function default_fromMsg(msg) { - return msg; - }, - /** - * Import a value from C. - * - * In this default implementation, return the value - * unchanged. - */ - importFromC: function default_importFromC(value) { - return value; - }, - - /** - * A pointer/array used to pass data to the foreign function. - */ - get in_ptr() { - delete this.in_ptr; - let ptr_t = new PtrType( - "[in] " + this.name + "*", - this.implementation.ptr, - this - ); - Object.defineProperty(this, "in_ptr", { - get() { - return ptr_t; - }, - }); - return ptr_t; - }, - - /** - * A pointer/array used to receive data from the foreign function. - */ - get out_ptr() { - delete this.out_ptr; - let ptr_t = new PtrType( - "[out] " + this.name + "*", - this.implementation.ptr, - this - ); - Object.defineProperty(this, "out_ptr", { - get() { - return ptr_t; - }, - }); - return ptr_t; - }, - - /** - * A pointer/array used to both pass data to the foreign function - * and receive data from the foreign function. - * - * Whenever possible, prefer using |in_ptr| or |out_ptr|, which - * are generally faster. - */ - get inout_ptr() { - delete this.inout_ptr; - let ptr_t = new PtrType( - "[inout] " + this.name + "*", - this.implementation.ptr, - this - ); - Object.defineProperty(this, "inout_ptr", { - get() { - return ptr_t; - }, - }); - return ptr_t; - }, - - /** - * Attach a finalizer to a type. - */ - releaseWith: function releaseWith(finalizer) { - let parent = this; - let type = this.withName("[auto " + this.name + ", " + finalizer + "] "); - type.importFromC = function importFromC(value, operation) { - return ctypes.CDataFinalizer( - parent.importFromC(value, operation), - finalizer - ); - }; - return type; - }, - - /** - * Lazy variant of releaseWith. - * Attach a finalizer lazily to a type. - * - * @param {function} getFinalizer The function that - * returns finalizer lazily. - */ - releaseWithLazy: function releaseWithLazy(getFinalizer) { - let parent = this; - let type = this.withName("[auto " + this.name + ", (lazy)] "); - type.importFromC = function importFromC(value, operation) { - return ctypes.CDataFinalizer( - parent.importFromC(value, operation), - getFinalizer() - ); - }; - return type; - }, - - /** - * Return an alias to a type with a different name. - */ - withName: function withName(name) { - return Object.create(this, { name: { value: name } }); - }, - - /** - * Cast a C value to |this| type. - * - * Throw an error if the value cannot be casted. - */ - cast: function cast(value) { - return ctypes.cast(value, this.implementation); - }, - - /** - * Return the number of bytes in a value of |this| type. - * - * This may not be defined, e.g. for |void_t|, array types - * without length, etc. - */ - get size() { - return this.implementation.size; - }, -}; - -/** - * Utility function used to determine whether an object is a typed array - */ -var isTypedArray = function isTypedArray(obj) { - return obj != null && typeof obj == "object" && "byteOffset" in obj; -}; -exports.isTypedArray = isTypedArray; - -/** - * Utility function used to determine whether an object is an ArrayBuffer. - */ -var isArrayBuffer = function(obj) { - return ( - obj != null && - typeof obj == "object" && - obj.constructor.name == "ArrayBuffer" - ); -}; -exports.isArrayBuffer = isArrayBuffer; - -/** - * A |Type| of pointers. - * - * @param {string} name The name of this type. - * @param {CType} implementation The type of this pointer. - * @param {Type} targetType The target type. - */ -function PtrType(name, implementation, targetType) { - Type.call(this, name, implementation); - if (targetType == null || !(targetType instanceof Type)) { - throw new TypeError("targetType must be an instance of Type"); - } - /** - * The type of values targeted by this pointer type. - */ - Object.defineProperty(this, "targetType", { - value: targetType, - }); -} -PtrType.prototype = Object.create(Type.prototype); - -/** - * Convert a value to a pointer. - * - * Protocol: - * - |null| returns |null| - * - a string returns |{string: value}| - * - a typed array returns |{ptr: address_of_buffer}| - * - a C array returns |{ptr: address_of_buffer}| - * everything else raises an error - */ -PtrType.prototype.toMsg = function ptr_toMsg(value) { - if (value == null) { - return null; - } - if (typeof value == "string") { - return { string: value }; - } - if (isTypedArray(value)) { - // Automatically transfer typed arrays - return new Meta({ data: value }, { transfers: [value.buffer] }); - } - if (isArrayBuffer(value)) { - // Automatically transfer array buffers - return new Meta({ data: value }, { transfers: [value] }); - } - let normalized; - if ("addressOfElement" in value) { - // C array - normalized = value.addressOfElement(0); - } else if ("isNull" in value) { - // C pointer - normalized = value; - } else { - throw new TypeError("Value " + value + " cannot be converted to a pointer"); - } - let cast = Type.uintptr_t.cast(normalized); - return { ptr: cast.value.toString() }; -}; - -/** - * Convert a message back to a pointer. - */ -PtrType.prototype.fromMsg = function ptr_fromMsg(msg) { - if (msg == null) { - return null; - } - if ("string" in msg) { - return msg.string; - } - if ("data" in msg) { - return msg.data; - } - if ("ptr" in msg) { - let address = ctypes.uintptr_t(msg.ptr); - return this.cast(address); - } - throw new TypeError( - "Message " + msg.toSource() + " does not represent a pointer" - ); -}; - -exports.Type = Type; - -/* - * Some values are large integers on 64 bit platforms. Unfortunately, - * in practice, 64 bit integers cannot be manipulated in JS. We - * therefore project them to regular numbers whenever possible. - */ - -var projectLargeInt = function projectLargeInt(x) { - let str = x.toString(); - let rv = parseInt(str, 10); - if (rv.toString() !== str) { - throw new TypeError("Number " + str + " cannot be projected to a double"); - } - return rv; -}; -var projectLargeUInt = function projectLargeUInt(x) { - return projectLargeInt(x); -}; -var projectValue = function projectValue(x) { - if (!(x instanceof ctypes.CData)) { - return x; - } - if (!("value" in x)) { - // Sanity check - throw new TypeError("Number " + x.toSource() + " has no field |value|"); - } - return x.value; -}; - -function projector(type, signed) { - LOG( - "Determining best projection for", - type, - "(size: ", - type.size, - ")", - signed ? "signed" : "unsigned" - ); - if (type instanceof Type) { - type = type.implementation; - } - if (!type.size) { - throw new TypeError("Argument is not a proper C type"); - } - // Determine if type is projected to Int64/Uint64 - if ( - type.size == 8 || // Usual case - // The following cases have special treatment in js-ctypes - // Regardless of their size, the value getter returns - // a Int64/Uint64 - type == ctypes.size_t || // Special cases - type == ctypes.ssize_t || - type == ctypes.intptr_t || - type == ctypes.uintptr_t || - type == ctypes.off_t - ) { - if (signed) { - LOG("Projected as a large signed integer"); - return projectLargeInt; - } - LOG("Projected as a large unsigned integer"); - return projectLargeUInt; - } - LOG("Projected as a regular number"); - return projectValue; -} -exports.projectValue = projectValue; - -/** - * Get the appropriate type for an unsigned int of the given size. - * - * This function is useful to define types such as |mode_t| whose - * actual width depends on the OS/platform. - * - * @param {number} size The number of bytes requested. - */ -Type.uintn_t = function uintn_t(size) { - switch (size) { - case 1: - return Type.uint8_t; - case 2: - return Type.uint16_t; - case 4: - return Type.uint32_t; - case 8: - return Type.uint64_t; - default: - throw new Error( - "Cannot represent unsigned integers of " + size + " bytes" - ); - } -}; - -/** - * Get the appropriate type for an signed int of the given size. - * - * This function is useful to define types such as |mode_t| whose - * actual width depends on the OS/platform. - * - * @param {number} size The number of bytes requested. - */ -Type.intn_t = function intn_t(size) { - switch (size) { - case 1: - return Type.int8_t; - case 2: - return Type.int16_t; - case 4: - return Type.int32_t; - case 8: - return Type.int64_t; - default: - throw new Error("Cannot represent integers of " + size + " bytes"); - } -}; - -/** - * Actual implementation of common C types. - */ - -/** - * The void value. - */ -Type.void_t = new Type("void", ctypes.void_t); - -/** - * Shortcut for |void*|. - */ -Type.voidptr_t = new PtrType("void*", ctypes.voidptr_t, Type.void_t); - -// void* is a special case as we can cast any pointer to/from it -// so we have to shortcut |in_ptr|/|out_ptr|/|inout_ptr| and -// ensure that js-ctypes' casting mechanism is invoked directly -["in_ptr", "out_ptr", "inout_ptr"].forEach(function(key) { - Object.defineProperty(Type.void_t, key, { - value: Type.voidptr_t, - }); -}); - -/** - * A Type of integers. - * - * @param {string} name The name of this type. - * @param {CType} implementation The underlying js-ctypes implementation. - * @param {bool} signed |true| if this is a type of signed integers, - * |false| otherwise. - * - * @constructor - */ -function IntType(name, implementation, signed) { - Type.call(this, name, implementation); - this.importFromC = projector(implementation, signed); - this.project = this.importFromC; -} -IntType.prototype = Object.create(Type.prototype); -IntType.prototype.toMsg = function toMsg(value) { - if (typeof value == "number") { - return value; - } - return this.project(value); -}; - -/** - * A C char (one byte) - */ -Type.char = new Type("char", ctypes.char); - -/** - * A C wide char (two bytes) - */ -Type.char16_t = new Type("char16_t", ctypes.char16_t); - -/** - * Base string types. - */ -Type.cstring = Type.char.in_ptr.withName("[in] C string"); -Type.wstring = Type.char16_t.in_ptr.withName("[in] wide string"); -Type.out_cstring = Type.char.out_ptr.withName("[out] C string"); -Type.out_wstring = Type.char16_t.out_ptr.withName("[out] wide string"); - -/** - * A C integer (8-bits). - */ -Type.int8_t = new IntType("int8_t", ctypes.int8_t, true); - -Type.uint8_t = new IntType("uint8_t", ctypes.uint8_t, false); - -/** - * A C integer (16-bits). - * - * Also known as WORD under Windows. - */ -Type.int16_t = new IntType("int16_t", ctypes.int16_t, true); - -Type.uint16_t = new IntType("uint16_t", ctypes.uint16_t, false); - -/** - * A C integer (32-bits). - * - * Also known as DWORD under Windows. - */ -Type.int32_t = new IntType("int32_t", ctypes.int32_t, true); - -Type.uint32_t = new IntType("uint32_t", ctypes.uint32_t, false); - -/** - * A C integer (64-bits). - */ -Type.int64_t = new IntType("int64_t", ctypes.int64_t, true); - -Type.uint64_t = new IntType("uint64_t", ctypes.uint64_t, false); - -/** - * A C integer - * - * Size depends on the platform. - */ -Type.int = Type.intn_t(ctypes.int.size).withName("int"); - -Type.unsigned_int = Type.intn_t(ctypes.unsigned_int.size).withName( - "unsigned int" -); - -/** - * A C long integer. - * - * Size depends on the platform. - */ -Type.long = Type.intn_t(ctypes.long.size).withName("long"); - -Type.unsigned_long = Type.intn_t(ctypes.unsigned_long.size).withName( - "unsigned long" -); - -/** - * An unsigned integer with the same size as a pointer. - * - * Used to cast a pointer to an integer, whenever necessary. - */ -Type.uintptr_t = Type.uintn_t(ctypes.uintptr_t.size).withName("uintptr_t"); - -/** - * A boolean. - * Implemented as a C integer. - */ -Type.bool = Type.int.withName("bool"); -Type.bool.importFromC = function projectBool(x) { - return !!x.value; -}; - -/** - * A user identifier. - * - * Implemented as a C integer. - */ -Type.uid_t = Type.int.withName("uid_t"); - -/** - * A group identifier. - * - * Implemented as a C integer. - */ -Type.gid_t = Type.int.withName("gid_t"); - -/** - * An offset (positive or negative). - * - * Implemented as a C integer. - */ -Type.off_t = new IntType("off_t", ctypes.off_t, true); - -/** - * A size (positive). - * - * Implemented as a C size_t. - */ -Type.size_t = new IntType("size_t", ctypes.size_t, false); - -/** - * An offset (positive or negative). - * Implemented as a C integer. - */ -Type.ssize_t = new IntType("ssize_t", ctypes.ssize_t, true); - -/** - * Encoding/decoding strings - */ -Type.uencoder = new Type("uencoder", ctypes.StructType("uencoder")); -Type.udecoder = new Type("udecoder", ctypes.StructType("udecoder")); - -/** - * Utility class, used to build a |struct| type - * from a set of field names, types and offsets. - * - * @param {string} name The name of the |struct| type. - * @param {number} size The total size of the |struct| type in bytes. - */ -function HollowStructure(name, size) { - if (!name) { - throw new TypeError("HollowStructure expects a name"); - } - if (!size || size < 0) { - throw new TypeError("HollowStructure expects a (positive) size"); - } - - // A mapping from offsets in the struct to name/type pairs - // (or nothing if no field starts at that offset). - this.offset_to_field_info = []; - - // The name of the struct - this.name = name; - - // The size of the struct, in bytes - this.size = size; - - // The number of paddings inserted so far. - // Used to give distinct names to padding fields. - this._paddings = 0; -} -HollowStructure.prototype = { - /** - * Add a field at a given offset. - * - * @param {number} offset The offset at which to insert the field. - * @param {string} name The name of the field. - * @param {CType|Type} type The type of the field. - */ - add_field_at: function add_field_at(offset, name, type) { - if (offset == null) { - throw new TypeError("add_field_at requires a non-null offset"); - } - if (!name) { - throw new TypeError("add_field_at requires a non-null name"); - } - if (!type) { - throw new TypeError("add_field_at requires a non-null type"); - } - if (type instanceof Type) { - type = type.implementation; - } - if (this.offset_to_field_info[offset]) { - throw new Error( - "HollowStructure " + - this.name + - " already has a field at offset " + - offset - ); - } - if (offset + type.size > this.size) { - throw new Error( - "HollowStructure " + - this.name + - " cannot place a value of type " + - type + - " at offset " + - offset + - " without exceeding its size of " + - this.size - ); - } - let field = { name, type }; - this.offset_to_field_info[offset] = field; - }, - - /** - * Create a pseudo-field that will only serve as padding. - * - * @param {number} size The number of bytes in the field. - * @return {Object} An association field-name => field-type, - * as expected by |ctypes.StructType|. - */ - _makePaddingField: function makePaddingField(size) { - let field = {}; - field["padding_" + this._paddings] = ctypes.ArrayType(ctypes.uint8_t, size); - this._paddings++; - return field; - }, - - /** - * Convert this |HollowStructure| into a |Type|. - */ - getType: function getType() { - // Contents of the structure, in the format expected - // by ctypes.StructType. - let struct = []; - - let i = 0; - while (i < this.size) { - let currentField = this.offset_to_field_info[i]; - if (!currentField) { - // No field was specified at this offset, we need to - // introduce some padding. - - // Firstly, determine how many bytes of padding - let padding_length = 1; - while ( - i + padding_length < this.size && - !this.offset_to_field_info[i + padding_length] - ) { - ++padding_length; - } - - // Then add the padding - struct.push(this._makePaddingField(padding_length)); - - // And proceed - i += padding_length; - } else { - // We have a field at this offset. - - // Firstly, ensure that we do not have two overlapping fields - for (let j = 1; j < currentField.type.size; ++j) { - let candidateField = this.offset_to_field_info[i + j]; - if (candidateField) { - throw new Error( - "Fields " + - currentField.name + - " and " + - candidateField.name + - " overlap at position " + - (i + j) - ); - } - } - - // Then add the field - let field = {}; - field[currentField.name] = currentField.type; - struct.push(field); - - // And proceed - i += currentField.type.size; - } - } - let result = new Type(this.name, ctypes.StructType(this.name, struct)); - if (result.implementation.size != this.size) { - throw new Error( - "Wrong size for type " + - this.name + - ": expected " + - this.size + - ", found " + - result.implementation.size + - " (" + - result.implementation.toSource() + - ")" - ); - } - return result; - }, -}; -exports.HollowStructure = HollowStructure; - -/** - * Representation of a native library. - * - * The native library is opened lazily, during the first call to its - * field |library| or whenever accessing one of the methods imported - * with declareLazyFFI. - * - * @param {string} name A human-readable name for the library. Used - * for debugging and error reporting. - * @param {string...} candidates A list of system libraries that may - * represent this library. Used e.g. to try different library names - * on distinct operating systems ("libxul", "XUL", etc.). - * - * @constructor - */ -function Library(name, ...candidates) { - this.name = name; - this._candidates = candidates; -} -Library.prototype = Object.freeze({ - /** - * The native library as a js-ctypes object. - * - * @throws {Error} If none of the candidate libraries could be opened. - */ - get library() { - let library; - delete this.library; - for (let candidate of this._candidates) { - try { - library = ctypes.open(candidate); - break; - } catch (ex) { - LOG("Could not open library", candidate, ex); - } - } - this._candidates = null; - if (library) { - Object.defineProperty(this, "library", { - value: library, - }); - Object.freeze(this); - return library; - } - let error = new Error("Could not open library " + this.name); - Object.defineProperty(this, "library", { - get() { - throw error; - }, - }); - Object.freeze(this); - throw error; - }, - - /** - * Declare a function, lazily. - * - * @param {object} The object containing the function as a field. - * @param {string} The name of the field containing the function. - * @param {string} symbol The name of the function, as defined in the - * library. - * @param {ctypes.abi} abi The abi to use, or |null| for default. - * @param {Type} returnType The type of values returned by the function. - * @param {...Type} argTypes The type of arguments to the function. - */ - declareLazyFFI(object, field, ...args) { - let lib = this; - Object.defineProperty(object, field, { - get() { - delete this[field]; - let ffi = declareFFI(lib.library, ...args); - if (ffi) { - return (this[field] = ffi); - } - return undefined; - }, - configurable: true, - enumerable: true, - }); - }, - - /** - * Define a js-ctypes function lazily using ctypes method declare. - * - * @param {object} The object containing the function as a field. - * @param {string} The name of the field containing the function. - * @param {string} symbol The name of the function, as defined in the - * library. - * @param {ctypes.abi} abi The abi to use, or |null| for default. - * @param {ctypes.CType} returnType The type of values returned by the function. - * @param {...ctypes.CType} argTypes The type of arguments to the function. - */ - declareLazy(object, field, ...args) { - let lib = this; - Object.defineProperty(object, field, { - get() { - delete this[field]; - let ffi = lib.library.declare(...args); - if (ffi) { - return (this[field] = ffi); - } - return undefined; - }, - configurable: true, - enumerable: true, - }); - }, - - /** - * Define a js-ctypes function lazily using ctypes method declare, - * with a fallback library to use if this library can't be opened - * or the function cannot be declared. - * - * @param {fallbacklibrary} The fallback Library object. - * @param {object} The object containing the function as a field. - * @param {string} The name of the field containing the function. - * @param {string} symbol The name of the function, as defined in the - * library. - * @param {ctypes.abi} abi The abi to use, or |null| for default. - * @param {ctypes.CType} returnType The type of values returned by the function. - * @param {...ctypes.CType} argTypes The type of arguments to the function. - */ - declareLazyWithFallback(fallbacklibrary, object, field, ...args) { - let lib = this; - Object.defineProperty(object, field, { - get() { - delete this[field]; - try { - let ffi = lib.library.declare(...args); - if (ffi) { - return (this[field] = ffi); - } - } catch (ex) { - // Use the fallback library and get the symbol from there. - fallbacklibrary.declareLazy(object, field, ...args); - return object[field]; - } - return undefined; - }, - configurable: true, - enumerable: true, - }); - }, - - toString() { - return "[Library " + this.name + "]"; - }, -}); -exports.Library = Library; - -/** - * Declare a function through js-ctypes - * - * @param {ctypes.library} lib The ctypes library holding the function. - * @param {string} symbol The name of the function, as defined in the - * library. - * @param {ctypes.abi} abi The abi to use, or |null| for default. - * @param {Type} returnType The type of values returned by the function. - * @param {...Type} argTypes The type of arguments to the function. - * - * @return null if the function could not be defined (generally because - * it does not exist), or a JavaScript wrapper performing the call to C - * and any type conversion required. - */ -var declareFFI = function declareFFI( - lib, - symbol, - abi, - returnType /* , argTypes ...*/ -) { - LOG("Attempting to declare FFI ", symbol); - // We guard agressively, to avoid any late surprise - if (typeof symbol != "string") { - throw new TypeError("declareFFI expects as first argument a string"); - } - abi = abi || ctypes.default_abi; - if (Object.prototype.toString.call(abi) != "[object CABI]") { - // Note: This is the only known manner of checking whether an object - // is an abi. - throw new TypeError("declareFFI expects as second argument an abi or null"); - } - if (!returnType.importFromC) { - throw new TypeError( - "declareFFI expects as third argument an instance of Type" - ); - } - let signature = [symbol, abi]; - for (let i = 3; i < arguments.length; ++i) { - let current = arguments[i]; - if (!current) { - throw new TypeError( - "Missing type for argument " + (i - 3) + " of symbol " + symbol - ); - } - // Ellipsis for variadic arguments. - if (current == "...") { - if (i != arguments.length - 1) { - throw new TypeError("Variadic ellipsis must be the last argument"); - } - signature.push(current); - continue; - } - if (!current.implementation) { - throw new TypeError( - "Missing implementation for argument " + - (i - 3) + - " of symbol " + - symbol + - " ( " + - current.name + - " )" - ); - } - signature.push(current.implementation); - } - try { - let fun = lib.declare.apply(lib, signature); - let result = function ffi(...args) { - for (let i = 0; i < args.length; i++) { - if (typeof args[i] == "undefined") { - throw new TypeError( - "Argument " + i + " of " + symbol + " is undefined" - ); - } - } - let result = fun.apply(fun, args); - return returnType.importFromC(result, symbol); - }; - LOG("Function", symbol, "declared"); - return result; - } catch (x) { - // Note: Not being able to declare a function is normal. - // Some functions are OS (or OS version)-specific. - LOG("Could not declare function ", symbol, x); - return null; - } -}; -exports.declareFFI = declareFFI; - -/** - * Define a lazy getter to a js-ctypes function using declareFFI. - * - * @param {object} The object containing the function as a field. - * @param {string} The name of the field containing the function. - * @param {ctypes.library} lib The ctypes library holding the function. - * @param {string} symbol The name of the function, as defined in the - * library. - * @param {ctypes.abi} abi The abi to use, or |null| for default. - * @param {Type} returnType The type of values returned by the function. - * @param {...Type} argTypes The type of arguments to the function. - */ -function declareLazyFFI(object, field, ...declareFFIArgs) { - Object.defineProperty(object, field, { - get() { - delete this[field]; - let ffi = declareFFI(...declareFFIArgs); - if (ffi) { - return (this[field] = ffi); - } - return undefined; - }, - configurable: true, - enumerable: true, - }); -} -exports.declareLazyFFI = declareLazyFFI; - -/** - * Define a lazy getter to a js-ctypes function using ctypes method declare. - * - * @param {object} The object containing the function as a field. - * @param {string} The name of the field containing the function. - * @param {ctypes.library} lib The ctypes library holding the function. - * @param {string} symbol The name of the function, as defined in the - * library. - * @param {ctypes.abi} abi The abi to use, or |null| for default. - * @param {ctypes.CType} returnType The type of values returned by the function. - * @param {...ctypes.CType} argTypes The type of arguments to the function. - */ -function declareLazy(object, field, lib, ...declareArgs) { - Object.defineProperty(object, field, { - get() { - delete this[field]; - try { - let ffi = lib.declare(...declareArgs); - return (this[field] = ffi); - } catch (ex) { - // The symbol doesn't exist - return undefined; - } - }, - configurable: true, - }); -} -exports.declareLazy = declareLazy; - -/** - * Utility function used to sanity check buffer and length arguments. The - * buffer must be a Typed Array. - * - * @param {Typed array} candidate The buffer. - * @param {number} bytes The number of bytes that |candidate| should contain. - * - * @return number The bytes argument clamped to the length of the buffer. - */ -function normalizeBufferArgs(candidate, bytes) { - if (!candidate) { - throw new TypeError("Expecting a Typed Array"); - } - if (!isTypedArray(candidate)) { - throw new TypeError("Expecting a Typed Array"); - } - if (bytes == null) { - bytes = candidate.byteLength; - } else if (candidate.byteLength < bytes) { - throw new TypeError( - "Buffer is too short. I need at least " + - bytes + - " bytes but I have only " + - candidate.byteLength + - "bytes" - ); - } - return bytes; -} -exports.normalizeBufferArgs = normalizeBufferArgs; - -// /////////////////// OS interactions - -/** - * An OS error. - * - * This class is provided mostly for type-matching. If you need more - * details about an error, you should use the platform-specific error - * codes provided by subclasses of |OS.Shared.Error|. - * - * @param {string} operation The operation that failed. - * @param {string=} path The path of the file on which the operation failed, - * or nothing if there was no file involved in the failure. - * - * @constructor - */ -function OSError(operation, path = "") { - Error.call(this); - this.operation = operation; - this.path = path; -} -OSError.prototype = Object.create(Error.prototype); -exports.OSError = OSError; - -// /////////////////// Temporary boilerplate -// Boilerplate, to simplify the transition to require() -// Do not rely upon this symbol, it will disappear with -// bug 883050. -exports.OS = { - Constants: exports.Constants, - Shared: { - LOG, - clone, - Type, - HollowStructure, - Error: OSError, - declareFFI, - projectValue, - isTypedArray, - defineLazyGetter, - }, -}; - -Object.defineProperty(exports.OS.Shared, "DEBUG", { - get() { - return Config.DEBUG; - }, - set(x) { - Config.DEBUG = x; - }, -}); -Object.defineProperty(exports.OS.Shared, "TEST", { - get() { - return Config.TEST; - }, - set(x) { - Config.TEST = x; - }, -}); - -// /////////////////// Permanent boilerplate -if (typeof Components != "undefined") { - // eslint-disable-next-line mozilla/reject-global-this - this.EXPORTED_SYMBOLS = EXPORTED_SYMBOLS; - for (let symbol of EXPORTED_SYMBOLS) { - // eslint-disable-next-line mozilla/reject-global-this - this[symbol] = exports[symbol]; - } -}
deleted file mode 100644 --- a/toolkit/components/osfile/modules/osfile_shared_front.js +++ /dev/null @@ -1,607 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Code shared by OS.File front-ends. - * - * This code is meant to be included by another library. It is also meant to - * be executed only on a worker thread. - */ - -/* eslint-env node */ -/* global OS */ - -if (typeof Components != "undefined") { - throw new Error("osfile_shared_front.js cannot be used from the main thread"); -} -(function(exports) { - var SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm"); - var Path = require("resource://gre/modules/osfile/ospath.jsm"); - var Lz4 = require("resource://gre/modules/lz4.js"); - SharedAll.LOG.bind(SharedAll, "Shared front-end"); - var clone = SharedAll.clone; - - /** - * Code shared by implementations of File. - * - * @param {*} fd An OS-specific file handle. - * @param {string} path File path of the file handle, used for error-reporting. - * @constructor - */ - var AbstractFile = function AbstractFile(fd, path) { - this._fd = fd; - if (!path) { - throw new TypeError("path is expected"); - } - this._path = path; - }; - - AbstractFile.prototype = { - /** - * Return the file handle. - * - * @throw OS.File.Error if the file has been closed. - */ - get fd() { - if (this._fd) { - return this._fd; - } - throw OS.File.Error.closed("accessing file", this._path); - }, - /** - * Read bytes from this file to a new buffer. - * - * @param {number=} maybeBytes (deprecated, please use options.bytes) - * @param {JSON} options - * @return {Uint8Array} An array containing the bytes read. - */ - read: function read(maybeBytes, options = {}) { - if (typeof maybeBytes === "object") { - // Caller has skipped `maybeBytes` and provided an options object. - options = clone(maybeBytes); - maybeBytes = null; - } else { - options = options || {}; - } - let bytes = options.bytes || undefined; - if (bytes === undefined) { - bytes = maybeBytes == null ? this.stat().size : maybeBytes; - } - let buffer = new Uint8Array(bytes); - let pos = 0; - while (pos < bytes) { - let length = bytes - pos; - let view = new DataView(buffer.buffer, pos, length); - let chunkSize = this._read(view, length, options); - if (chunkSize == 0) { - break; - } - pos += chunkSize; - } - if (pos == bytes) { - return buffer; - } - return buffer.subarray(0, pos); - }, - - /** - * Write bytes from a buffer to this file. - * - * Note that, by default, this function may perform several I/O - * operations to ensure that the buffer is fully written. - * - * @param {Typed array} buffer The buffer in which the the bytes are - * stored. The buffer must be large enough to accomodate |bytes| bytes. - * @param {*=} options Optionally, an object that may contain the - * following fields: - * - {number} bytes The number of |bytes| to write from the buffer. If - * unspecified, this is |buffer.byteLength|. - * - * @return {number} The number of bytes actually written. - */ - write: function write(buffer, options = {}) { - let bytes = SharedAll.normalizeBufferArgs( - buffer, - "bytes" in options ? options.bytes : undefined - ); - let pos = 0; - while (pos < bytes) { - let length = bytes - pos; - let view = new DataView(buffer.buffer, buffer.byteOffset + pos, length); - let chunkSize = this._write(view, length, options); - pos += chunkSize; - } - return pos; - }, - }; - - /** - * Creates and opens a file with a unique name. By default, generate a random HEX number and use it to create a unique new file name. - * - * @param {string} path The path to the file. - * @param {*=} options Additional options for file opening. This - * implementation interprets the following fields: - * - * - {number} humanReadable If |true|, create a new filename appending a decimal number. ie: filename-1.ext, filename-2.ext. - * If |false| use HEX numbers ie: filename-A65BC0.ext - * - {number} maxReadableNumber Used to limit the amount of tries after a failed - * file creation. Default is 20. - * - * @return {Object} contains A file object{file} and the path{path}. - * @throws {OS.File.Error} If the file could not be opened. - */ - AbstractFile.openUnique = function openUnique(path, options = {}) { - let mode = { - create: true, - }; - - let dirName = Path.dirname(path); - let leafName = Path.basename(path); - let lastDotCharacter = leafName.lastIndexOf("."); - let fileName = leafName.substring( - 0, - lastDotCharacter != -1 ? lastDotCharacter : leafName.length - ); - let suffix = - lastDotCharacter != -1 ? leafName.substring(lastDotCharacter) : ""; - let uniquePath = ""; - let maxAttempts = options.maxAttempts || 99; - let humanReadable = !!options.humanReadable; - const HEX_RADIX = 16; - // We produce HEX numbers between 0 and 2^24 - 1. - const MAX_HEX_NUMBER = 16777215; - - try { - return { - path, - file: OS.File.open(path, mode), - }; - } catch (ex) { - if (ex instanceof OS.File.Error && ex.becauseExists) { - for (let i = 0; i < maxAttempts; ++i) { - try { - if (humanReadable) { - uniquePath = Path.join( - dirName, - fileName + "-" + (i + 1) + suffix - ); - } else { - let hexNumber = Math.floor( - Math.random() * MAX_HEX_NUMBER - ).toString(HEX_RADIX); - uniquePath = Path.join( - dirName, - fileName + "-" + hexNumber + suffix - ); - } - return { - path: uniquePath, - file: OS.File.open(uniquePath, mode), - }; - } catch (ex) { - if (ex instanceof OS.File.Error && ex.becauseExists) { - // keep trying ... - } else { - throw ex; - } - } - } - throw OS.File.Error.exists("could not find an unused file name.", path); - } - throw ex; - } - }; - - /** - * Code shared by iterators. - */ - AbstractFile.AbstractIterator = function AbstractIterator() {}; - AbstractFile.AbstractIterator.prototype = { - /** - * Allow iterating with |for-of| - */ - [Symbol.iterator]() { - return this; - }, - /** - * Apply a function to all elements of the directory sequentially. - * - * @param {Function} cb This function will be applied to all entries - * of the directory. It receives as arguments - * - the OS.File.Entry corresponding to the entry; - * - the index of the entry in the enumeration; - * - the iterator itself - calling |close| on the iterator stops - * the loop. - */ - forEach: function forEach(cb) { - let index = 0; - for (let entry of this) { - cb(entry, index++, this); - } - }, - /** - * Return several entries at once. - * - * Entries are returned in the same order as a walk with |forEach| or - * |for(...)|. - * - * @param {number=} length If specified, the number of entries - * to return. If unspecified, return all remaining entries. - * @return {Array} An array containing the next |length| entries, or - * less if the iteration contains less than |length| entries left. - */ - nextBatch: function nextBatch(length) { - let array = []; - let i = 0; - for (let entry of this) { - array.push(entry); - if (++i >= length) { - return array; - } - } - return array; - }, - }; - - /** - * Utility function shared by implementations of |OS.File.open|: - * extract read/write/trunc/create/existing flags from a |mode| - * object. - * - * @param {*=} mode An object that may contain fields |read|, - * |write|, |truncate|, |create|, |existing|. These fields - * are interpreted only if true-ish. - * @return {{read:bool, write:bool, trunc:bool, create:bool, - * existing:bool}} an object recapitulating the options set - * by |mode|. - * @throws {TypeError} If |mode| contains other fields, or - * if it contains both |create| and |truncate|, or |create| - * and |existing|. - */ - AbstractFile.normalizeOpenMode = function normalizeOpenMode(mode) { - let result = { - read: false, - write: false, - trunc: false, - create: false, - existing: false, - append: true, - }; - for (let key in mode) { - let val = !!mode[key]; // bool cast. - switch (key) { - case "read": - result.read = val; - break; - case "write": - result.write = val; - break; - case "truncate": // fallthrough - case "trunc": - result.trunc = val; - result.write |= val; - break; - case "create": - result.create = val; - result.write |= val; - break; - case "existing": // fallthrough - case "exist": - result.existing = val; - break; - case "append": - result.append = val; - break; - default: - throw new TypeError("Mode " + key + " not understood"); - } - } - // Reject opposite modes - if (result.existing && result.create) { - throw new TypeError("Cannot specify both existing:true and create:true"); - } - if (result.trunc && result.create) { - throw new TypeError("Cannot specify both trunc:true and create:true"); - } - // Handle read/write - if (!result.write) { - result.read = true; - } - return result; - }; - - /** - * Return the contents of a file. - * - * @param {string} path The path to the file. - * @param {number=} bytes Optionally, an upper bound to the number of bytes - * to read. DEPRECATED - please use options.bytes instead. - * @param {object=} options Optionally, an object with some of the following - * fields: - * - {number} bytes An upper bound to the number of bytes to read. - * - {string} compression If "lz4" and if the file is compressed using the lz4 - * compression algorithm, decompress the file contents on the fly. - * - * @return {Uint8Array} A buffer holding the bytes - * and the number of bytes read from the file. - */ - AbstractFile.read = function read(path, bytes, options = {}) { - if (bytes && typeof bytes == "object") { - options = bytes; - bytes = options.bytes || null; - } - if ("encoding" in options && typeof options.encoding != "string") { - throw new TypeError("Invalid type for option encoding"); - } - if ("compression" in options && typeof options.compression != "string") { - throw new TypeError( - "Invalid type for option compression: " + options.compression - ); - } - if ("bytes" in options && typeof options.bytes != "number") { - throw new TypeError("Invalid type for option bytes"); - } - let file = exports.OS.File.open(path); - try { - let buffer = file.read(bytes, options); - if ("compression" in options) { - if (options.compression == "lz4") { - options = Object.create(options); - options.path = path; - buffer = Lz4.decompressFileContent(buffer, options); - } else { - throw OS.File.Error.invalidArgument("Compression"); - } - } - if (!("encoding" in options)) { - return buffer; - } - let decoder; - try { - decoder = new TextDecoder(options.encoding); - } catch (ex) { - if (ex instanceof RangeError) { - throw OS.File.Error.invalidArgument("Decode"); - } else { - throw ex; - } - } - return decoder.decode(buffer); - } finally { - file.close(); - } - }; - - /** - * Write a file, atomically. - * - * By opposition to a regular |write|, this operation ensures that, - * until the contents are fully written, the destination file is - * not modified. - * - * Limitation: In a few extreme cases (hardware failure during the - * write, user unplugging disk during the write, etc.), data may be - * corrupted. If your data is user-critical (e.g. preferences, - * application data, etc.), you may wish to consider adding options - * |tmpPath| and/or |flush| to reduce the likelihood of corruption, as - * detailed below. Note that no combination of options can be - * guaranteed to totally eliminate the risk of corruption. - * - * @param {string} path The path of the file to modify. - * @param {Typed Array | C pointer} buffer A buffer containing the bytes to write. - * @param {*=} options Optionally, an object determining the behavior - * of this function. This object may contain the following fields: - * - {number} bytes The number of bytes to write. If unspecified, - * |buffer.byteLength|. Required if |buffer| is a C pointer. - * - {string} tmpPath If |null| or unspecified, write all data directly - * to |path|. If specified, write all data to a temporary file called - * |tmpPath| and, once this write is complete, rename the file to - * replace |path|. Performing this additional operation is a little - * slower but also a little safer. - * - {bool} noOverwrite - If set, this function will fail if a file already - * exists at |path|. - * - {bool} flush - If |false| or unspecified, return immediately once the - * write is complete. If |true|, before writing, force the operating system - * to write its internal disk buffers to the disk. This is considerably slower - * (not just for the application but for the whole system) but also safer: - * if the system shuts down improperly (typically due to a kernel freeze - * or a power failure) or if the device is disconnected before the buffer - * is flushed, the file has more chances of not being corrupted. - * - {string} compression - If empty or unspecified, do not compress the file. - * If "lz4", compress the contents of the file atomically using lz4. For the - * time being, the container format is specific to Mozilla and cannot be read - * by means other than OS.File.read(..., { compression: "lz4"}) - * - {string} backupTo - If specified, backup the destination file as |backupTo|. - * Note that this function renames the destination file before overwriting it. - * If the process or the operating system freezes or crashes - * during the short window between these operations, - * the destination file will have been moved to its backup. - * - * @return {number} The number of bytes actually written. - */ - AbstractFile.writeAtomic = function writeAtomic(path, buffer, options = {}) { - // Verify that path is defined and of the correct type - if (typeof path != "string" || path == "") { - throw new TypeError("File path should be a (non-empty) string"); - } - let noOverwrite = options.noOverwrite; - if (noOverwrite && OS.File.exists(path)) { - throw OS.File.Error.exists("writeAtomic", path); - } - - if (typeof buffer == "string") { - // Normalize buffer to a C buffer by encoding it - buffer = new TextEncoder().encode(buffer); - } - - if ("compression" in options && options.compression == "lz4") { - buffer = Lz4.compressFileContent(buffer, options); - options = Object.create(options); - options.bytes = buffer.byteLength; - } - - let bytesWritten = 0; - - if (!options.tmpPath) { - if (options.backupTo) { - try { - OS.File.move(path, options.backupTo, { noCopy: true }); - } catch (ex) { - if (ex.becauseNoSuchFile) { - // The file doesn't exist, nothing to backup. - } else { - throw ex; - } - } - } - // Just write, without any renaming trick - let dest = OS.File.open(path, { write: true, truncate: true }); - try { - bytesWritten = dest.write(buffer, options); - if (options.flush) { - dest.flush(); - } - } finally { - dest.close(); - } - return bytesWritten; - } - - let tmpFile = OS.File.open(options.tmpPath, { - write: true, - truncate: true, - }); - try { - bytesWritten = tmpFile.write(buffer, options); - if (options.flush) { - tmpFile.flush(); - } - } catch (x) { - OS.File.remove(options.tmpPath); - throw x; - } finally { - tmpFile.close(); - } - - if (options.backupTo) { - try { - OS.File.move(path, options.backupTo, { noCopy: true }); - } catch (ex) { - if (ex.becauseNoSuchFile) { - // The file doesn't exist, nothing to backup. - } else { - throw ex; - } - } - } - - OS.File.move(options.tmpPath, path, { noCopy: true }); - return bytesWritten; - }; - - /** - * This function is used by removeDir to avoid calling lstat for each - * files under the specified directory. External callers should not call - * this function directly. - */ - AbstractFile.removeRecursive = function(path, options = {}) { - let iterator = new OS.File.DirectoryIterator(path); - if (!iterator.exists()) { - if (!("ignoreAbsent" in options) || options.ignoreAbsent) { - return; - } - } - - try { - for (let entry of iterator) { - if (entry.isDir) { - if (entry.isLink) { - // Unlike Unix symlinks, NTFS junctions or NTFS symlinks to - // directories are directories themselves. OS.File.remove() - // will not work for them. - OS.File.removeEmptyDir(entry.path, options); - } else { - // Normal directories. - AbstractFile.removeRecursive(entry.path, options); - } - } else { - // NTFS symlinks to files, Unix symlinks, or regular files. - OS.File.remove(entry.path, options); - } - } - } finally { - iterator.close(); - } - - OS.File.removeEmptyDir(path); - }; - - /** - * Create a directory and, optionally, its parent directories. - * - * @param {string} path The name of the directory. - * @param {*=} options Additional options. - * - * - {string} from If specified, the call to |makeDir| creates all the - * ancestors of |path| that are descendants of |from|. Note that |path| - * must be a descendant of |from|, and that |from| and its existing - * subdirectories present in |path| must be user-writeable. - * Example: - * makeDir(Path.join(profileDir, "foo", "bar"), { from: profileDir }); - * creates directories profileDir/foo, profileDir/foo/bar - * - {bool} ignoreExisting If |false|, throw an error if the directory - * already exists. |true| by default. Ignored if |from| is specified. - * - {number} unixMode Under Unix, if specified, a file creation mode, - * as per libc function |mkdir|. If unspecified, dirs are - * created with a default mode of 0700 (dir is private to - * the user, the user can read, write and execute). Ignored under Windows - * or if the file system does not support file creation modes. - * - {C pointer} winSecurity Under Windows, if specified, security - * attributes as per winapi function |CreateDirectory|. If - * unspecified, use the default security descriptor, inherited from - * the parent directory. Ignored under Unix or if the file system - * does not support security descriptors. - */ - AbstractFile.makeDir = function(path, options = {}) { - let from = options.from; - if (!from) { - OS.File._makeDir(path, options); - return; - } - if (!path.startsWith(from)) { - // Apparently, `from` is not a parent of `path`. However, we may - // have false negatives due to non-normalized paths, e.g. - // "foo//bar" is a parent of "foo/bar/sna". - path = Path.normalize(path); - from = Path.normalize(from); - if (!path.startsWith(from)) { - throw new Error( - "Incorrect use of option |from|: " + - path + - " is not a descendant of " + - from - ); - } - } - let innerOptions = Object.create(options, { - ignoreExisting: { - value: true, - }, - }); - // Compute the elements that appear in |path| but not in |from|. - let items = Path.split(path).components.slice( - Path.split(from).components.length - ); - let current = from; - for (let item of items) { - current = Path.join(current, item); - OS.File._makeDir(current, innerOptions); - } - }; - - if (!exports.OS.Shared) { - exports.OS.Shared = {}; - } - exports.OS.Shared.AbstractFile = AbstractFile; -})(this);
deleted file mode 100644 --- a/toolkit/components/osfile/modules/osfile_unix_allthreads.jsm +++ /dev/null @@ -1,412 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * This module defines the thread-agnostic components of the Unix version - * of OS.File. It depends on the thread-agnostic cross-platform components - * of OS.File. - * - * It serves the following purposes: - * - open libc; - * - define OS.Unix.Error; - * - define a few constants and types that need to be defined on all platforms. - * - * This module can be: - * - opened from the main thread as a jsm module; - * - opened from a chrome worker through require(). - */ - -/* eslint-env node */ - -"use strict"; - -var SharedAll; -if (typeof Components != "undefined") { - // Module is opened as a jsm module - const { ctypes } = ChromeUtils.importESModule( - "resource://gre/modules/ctypes.sys.mjs" - ); - // eslint-disable-next-line mozilla/reject-global-this - this.ctypes = ctypes; - - SharedAll = ChromeUtils.import( - "resource://gre/modules/osfile/osfile_shared_allthreads.jsm" - ); - // eslint-disable-next-line mozilla/reject-global-this - this.exports = {}; -} else if (typeof module != "undefined" && typeof require != "undefined") { - // Module is loaded with require() - SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm"); -} else { - throw new Error( - "Please open this module with Component.utils.import or with require()" - ); -} - -SharedAll.LOG.bind(SharedAll, "Unix", "allthreads"); -var Const = SharedAll.Constants.libc; - -// Open libc -var libc = new SharedAll.Library( - "libc", - "libc.so", - "libSystem.B.dylib", - "a.out" -); -exports.libc = libc; - -// Define declareFFI -var declareFFI = SharedAll.declareFFI.bind(null, libc); -exports.declareFFI = declareFFI; - -// Define lazy binding -var LazyBindings = {}; -libc.declareLazy( - LazyBindings, - "strerror", - "strerror", - ctypes.default_abi, - /* return*/ ctypes.char.ptr, - /* errnum*/ ctypes.int -); - -/** - * A File-related error. - * - * To obtain a human-readable error message, use method |toString|. - * To determine the cause of the error, use the various |becauseX| - * getters. To determine the operation that failed, use field - * |operation|. - * - * Additionally, this implementation offers a field - * |unixErrno|, which holds the OS-specific error - * constant. If you need this level of detail, you may match the value - * of this field against the error constants of |OS.Constants.libc|. - * - * @param {string=} operation The operation that failed. If unspecified, - * the name of the calling function is taken to be the operation that - * failed. - * @param {number=} lastError The OS-specific constant detailing the - * reason of the error. If unspecified, this is fetched from the system - * status. - * @param {string=} path The file path that manipulated. If unspecified, - * assign the empty string. - * - * @constructor - * @extends {OS.Shared.Error} - */ -var OSError = function OSError( - operation = "unknown operation", - errno = ctypes.errno, - path = "" -) { - SharedAll.OSError.call(this, operation, path); - this.unixErrno = errno; -}; -OSError.prototype = Object.create(SharedAll.OSError.prototype); -OSError.prototype.toString = function toString() { - return ( - "Unix error " + - this.unixErrno + - " during operation " + - this.operation + - (this.path ? " on file " + this.path : "") + - " (" + - LazyBindings.strerror(this.unixErrno).readStringReplaceMalformed() + - ")" - ); -}; -OSError.prototype.toMsg = function toMsg() { - return OSError.toMsg(this); -}; - -/** - * |true| if the error was raised because a file or directory - * already exists, |false| otherwise. - */ -Object.defineProperty(OSError.prototype, "becauseExists", { - get: function becauseExists() { - return this.unixErrno == Const.EEXIST; - }, -}); -/** - * |true| if the error was raised because a file or directory - * does not exist, |false| otherwise. - */ -Object.defineProperty(OSError.prototype, "becauseNoSuchFile", { - get: function becauseNoSuchFile() { - return this.unixErrno == Const.ENOENT; - }, -}); - -/** - * |true| if the error was raised because a directory is not empty - * does not exist, |false| otherwise. - */ -Object.defineProperty(OSError.prototype, "becauseNotEmpty", { - get: function becauseNotEmpty() { - return this.unixErrno == Const.ENOTEMPTY; - }, -}); -/** - * |true| if the error was raised because a file or directory - * is closed, |false| otherwise. - */ -Object.defineProperty(OSError.prototype, "becauseClosed", { - get: function becauseClosed() { - return this.unixErrno == Const.EBADF; - }, -}); -/** - * |true| if the error was raised because permission is denied to - * access a file or directory, |false| otherwise. - */ -Object.defineProperty(OSError.prototype, "becauseAccessDenied", { - get: function becauseAccessDenied() { - return this.unixErrno == Const.EACCES; - }, -}); -/** - * |true| if the error was raised because some invalid argument was passed, - * |false| otherwise. - */ -Object.defineProperty(OSError.prototype, "becauseInvalidArgument", { - get: function becauseInvalidArgument() { - return this.unixErrno == Const.EINVAL; - }, -}); - -/** - * Serialize an instance of OSError to something that can be - * transmitted across threads (not necessarily a string). - */ -OSError.toMsg = function toMsg(error) { - return { - exn: "OS.File.Error", - fileName: error.moduleName, - lineNumber: error.lineNumber, - stack: error.moduleStack, - operation: error.operation, - unixErrno: error.unixErrno, - path: error.path, - }; -}; - -/** - * Deserialize a message back to an instance of OSError - */ -OSError.fromMsg = function fromMsg(msg) { - let error = new OSError(msg.operation, msg.unixErrno, msg.path); - error.stack = msg.stack; - error.fileName = msg.fileName; - error.lineNumber = msg.lineNumber; - return error; -}; -exports.Error = OSError; - -/** - * Code shared by implementations of File.Info on Unix - * - * @constructor - */ -var AbstractInfo = function AbstractInfo( - path, - isDir, - isSymLink, - size, - lastAccessDate, - lastModificationDate, - unixLastStatusChangeDate, - unixOwner, - unixGroup, - unixMode -) { - this._path = path; - this._isDir = isDir; - this._isSymlLink = isSymLink; - this._size = size; - this._lastAccessDate = lastAccessDate; - this._lastModificationDate = lastModificationDate; - this._unixLastStatusChangeDate = unixLastStatusChangeDate; - this._unixOwner = unixOwner; - this._unixGroup = unixGroup; - this._unixMode = unixMode; -}; - -AbstractInfo.prototype = { - /** - * The path of the file, used for error-reporting. - * - * @type {string} - */ - get path() { - return this._path; - }, - /** - * |true| if this file is a directory, |false| otherwise - */ - get isDir() { - return this._isDir; - }, - /** - * |true| if this file is a symbolink link, |false| otherwise - */ - get isSymLink() { - return this._isSymlLink; - }, - /** - * The size of the file, in bytes. - * - * Note that the result may be |NaN| if the size of the file cannot be - * represented in JavaScript. - * - * @type {number} - */ - get size() { - return this._size; - }, - /** - * The date of last access to this file. - * - * Note that the definition of last access may depend on the - * underlying operating system and file system. - * - * @type {Date} - */ - get lastAccessDate() { - return this._lastAccessDate; - }, - /** - * Return the date of last modification of this file. - */ - get lastModificationDate() { - return this._lastModificationDate; - }, - /** - * Return the date at which the status of this file was last modified - * (this is the date of the latest write/renaming/mode change/... - * of the file) - */ - get unixLastStatusChangeDate() { - return this._unixLastStatusChangeDate; - }, - /* - * Return the Unix owner of this file - */ - get unixOwner() { - return this._unixOwner; - }, - /* - * Return the Unix group of this file - */ - get unixGroup() { - return this._unixGroup; - }, - /* - * Return the Unix group of this file - */ - get unixMode() { - return this._unixMode; - }, -}; -exports.AbstractInfo = AbstractInfo; - -/** - * Code shared by implementations of File.DirectoryIterator.Entry on Unix - * - * @constructor - */ -var AbstractEntry = function AbstractEntry(isDir, isSymLink, name, path) { - this._isDir = isDir; - this._isSymlLink = isSymLink; - this._name = name; - this._path = path; -}; - -AbstractEntry.prototype = { - /** - * |true| if the entry is a directory, |false| otherwise - */ - get isDir() { - return this._isDir; - }, - /** - * |true| if the entry is a directory, |false| otherwise - */ - get isSymLink() { - return this._isSymlLink; - }, - /** - * The name of the entry - * @type {string} - */ - get name() { - return this._name; - }, - /** - * The full path to the entry - */ - get path() { - return this._path; - }, -}; -exports.AbstractEntry = AbstractEntry; - -// Special constants that need to be defined on all platforms - -exports.POS_START = Const.SEEK_SET; -exports.POS_CURRENT = Const.SEEK_CUR; -exports.POS_END = Const.SEEK_END; - -// Special types that need to be defined for communication -// between threads -var Type = Object.create(SharedAll.Type); -exports.Type = Type; - -/** - * Native paths - * - * Under Unix, expressed as C strings - */ -Type.path = Type.cstring.withName("[in] path"); -Type.out_path = Type.out_cstring.withName("[out] path"); - -// Special constructors that need to be defined on all threads -OSError.closed = function closed(operation, path) { - return new OSError(operation, Const.EBADF, path); -}; - -OSError.exists = function exists(operation, path) { - return new OSError(operation, Const.EEXIST, path); -}; - -OSError.noSuchFile = function noSuchFile(operation, path) { - return new OSError(operation, Const.ENOENT, path); -}; - -OSError.invalidArgument = function invalidArgument(operation) { - return new OSError(operation, Const.EINVAL); -}; - -var EXPORTED_SYMBOLS = [ - "declareFFI", - "libc", - "Error", - "AbstractInfo", - "AbstractEntry", - "Type", - "POS_START", - "POS_CURRENT", - "POS_END", -]; - -// ////////// Boilerplate -if (typeof Components != "undefined") { - // eslint-disable-next-line mozilla/reject-global-this - this.EXPORTED_SYMBOLS = EXPORTED_SYMBOLS; - for (let symbol of EXPORTED_SYMBOLS) { - // eslint-disable-next-line mozilla/reject-global-this - this[symbol] = exports[symbol]; - } -}
deleted file mode 100644 --- a/toolkit/components/osfile/modules/osfile_unix_back.js +++ /dev/null @@ -1,1051 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* eslint-env mozilla/chrome-worker, node */ -/* global OS */ - -// eslint-disable-next-line no-lone-blocks -{ - if (typeof Components != "undefined") { - // We do not wish osfile_unix_back.js to be used directly as a main thread - // module yet. When time comes, it will be loaded by a combination of - // a main thread front-end/worker thread implementation that makes sure - // that we are not executing synchronous IO code in the main thread. - - throw new Error( - "osfile_unix_back.js cannot be used from the main thread yet" - ); - } - (function(exports) { - "use strict"; - if (exports.OS && exports.OS.Unix && exports.OS.Unix.File) { - return; // Avoid double initialization - } - - let SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm"); - let SysAll = require("resource://gre/modules/osfile/osfile_unix_allthreads.jsm"); - SharedAll.LOG.bind(SharedAll, "Unix", "back"); - let libc = SysAll.libc; - let Const = SharedAll.Constants.libc; - - /** - * Initialize the Unix module. - * - * @param {function=} declareFFI - */ - // FIXME: Both |init| and |aDeclareFFI| are deprecated, we should remove them - let init = function init(aDeclareFFI) { - if (aDeclareFFI) { - aDeclareFFI.bind(null, libc); - } else { - SysAll.declareFFI; - } - SharedAll.declareLazyFFI; - - // Initialize types that require additional OS-specific - // support - either finalization or matching against - // OS-specific constants. - let Type = Object.create(SysAll.Type); - let SysFile = (exports.OS.Unix.File = { Type }); - - /** - * A file descriptor. - */ - Type.fd = Type.int.withName("fd"); - Type.fd.importFromC = function importFromC(fd_int) { - return ctypes.CDataFinalizer(fd_int, SysFile._close); - }; - - /** - * A C integer holding -1 in case of error or a file descriptor - * in case of success. - */ - Type.negativeone_or_fd = Type.fd.withName("negativeone_or_fd"); - Type.negativeone_or_fd.importFromC = function importFromC(fd_int) { - if (fd_int == -1) { - return -1; - } - return ctypes.CDataFinalizer(fd_int, SysFile._close); - }; - - /** - * A C integer holding -1 in case of error or a meaningless value - * in case of success. - */ - Type.negativeone_or_nothing = Type.int.withName("negativeone_or_nothing"); - - /** - * A C integer holding -1 in case of error or a positive integer - * in case of success. - */ - Type.negativeone_or_ssize_t = Type.ssize_t.withName( - "negativeone_or_ssize_t" - ); - - /** - * Various libc integer types - */ - Type.mode_t = Type.intn_t(Const.OSFILE_SIZEOF_MODE_T).withName("mode_t"); - Type.uid_t = Type.intn_t(Const.OSFILE_SIZEOF_UID_T).withName("uid_t"); - Type.gid_t = Type.intn_t(Const.OSFILE_SIZEOF_GID_T).withName("gid_t"); - - /** - * Type |time_t| - */ - Type.time_t = Type.intn_t(Const.OSFILE_SIZEOF_TIME_T).withName("time_t"); - - // Structure |dirent| - // Building this type is rather complicated, as its layout varies between - // variants of Unix. For this reason, we rely on a number of constants - // (computed in C from the C data structures) that give us the layout. - // The structure we compute looks like - // { int8_t[...] before_d_type; // ignored content - // int8_t d_type ; - // int8_t[...] before_d_name; // ignored content - // char[...] d_name; - // }; - { - let d_name_extra_size = 0; - if (Const.OSFILE_SIZEOF_DIRENT_D_NAME < 8) { - // d_name is defined like "char d_name[1];" on some platforms - // (e.g. Solaris), we need to give it more size for our structure. - d_name_extra_size = 256; - } - - let dirent = new SharedAll.HollowStructure( - "dirent", - Const.OSFILE_SIZEOF_DIRENT + d_name_extra_size - ); - if (Const.OSFILE_OFFSETOF_DIRENT_D_TYPE != undefined) { - // |dirent| doesn't have d_type on some platforms (e.g. Solaris). - dirent.add_field_at( - Const.OSFILE_OFFSETOF_DIRENT_D_TYPE, - "d_type", - ctypes.uint8_t - ); - } - dirent.add_field_at( - Const.OSFILE_OFFSETOF_DIRENT_D_NAME, - "d_name", - ctypes.ArrayType( - ctypes.char, - Const.OSFILE_SIZEOF_DIRENT_D_NAME + d_name_extra_size - ) - ); - - // We now have built |dirent|. - Type.dirent = dirent.getType(); - } - Type.null_or_dirent_ptr = new SharedAll.Type( - "null_of_dirent", - Type.dirent.out_ptr.implementation - ); - - // Structure |stat| - // Same technique - { - let stat = new SharedAll.HollowStructure( - "stat", - Const.OSFILE_SIZEOF_STAT - ); - stat.add_field_at( - Const.OSFILE_OFFSETOF_STAT_ST_MODE, - "st_mode", - Type.mode_t.implementation - ); - stat.add_field_at( - Const.OSFILE_OFFSETOF_STAT_ST_UID, - "st_uid", - Type.uid_t.implementation - ); - stat.add_field_at( - Const.OSFILE_OFFSETOF_STAT_ST_GID, - "st_gid", - Type.gid_t.implementation - ); - - // Here, things get complicated with different data structures. - // Some platforms have |time_t st_atime| and some platforms have - // |timespec st_atimespec|. However, since |timespec| starts with - // a |time_t|, followed by nanoseconds, we just cheat and pretend - // that everybody has |time_t st_atime|, possibly followed by padding - stat.add_field_at( - Const.OSFILE_OFFSETOF_STAT_ST_ATIME, - "st_atime", - Type.time_t.implementation - ); - stat.add_field_at( - Const.OSFILE_OFFSETOF_STAT_ST_MTIME, - "st_mtime", - Type.time_t.implementation - ); - stat.add_field_at( - Const.OSFILE_OFFSETOF_STAT_ST_CTIME, - "st_ctime", - Type.time_t.implementation - ); - - stat.add_field_at( - Const.OSFILE_OFFSETOF_STAT_ST_SIZE, - "st_size", - Type.off_t.implementation - ); - Type.stat = stat.getType(); - } - - // Structure |DIR| - if ("OSFILE_SIZEOF_DIR" in Const) { - // On platforms for which we need to access the fields of DIR - // directly (e.g. because certain functions are implemented - // as macros), we need to define DIR as a hollow structure. - let DIR = new SharedAll.HollowStructure("DIR", Const.OSFILE_SIZEOF_DIR); - - DIR.add_field_at( - Const.OSFILE_OFFSETOF_DIR_DD_FD, - "dd_fd", - Type.fd.implementation - ); - - Type.DIR = DIR.getType(); - } else { - // On other platforms, we keep DIR as a blackbox - Type.DIR = new SharedAll.Type("DIR", ctypes.StructType("DIR")); - } - - Type.null_or_DIR_ptr = Type.DIR.out_ptr.withName("null_or_DIR*"); - Type.null_or_DIR_ptr.importFromC = function importFromC(dir) { - if (dir == null || dir.isNull()) { - return null; - } - return ctypes.CDataFinalizer(dir, SysFile._close_dir); - }; - - // Structure |timeval| - { - let timeval = new SharedAll.HollowStructure( - "timeval", - Const.OSFILE_SIZEOF_TIMEVAL - ); - timeval.add_field_at( - Const.OSFILE_OFFSETOF_TIMEVAL_TV_SEC, - "tv_sec", - Type.long.implementation - ); - timeval.add_field_at( - Const.OSFILE_OFFSETOF_TIMEVAL_TV_USEC, - "tv_usec", - Type.long.implementation - ); - Type.timeval = timeval.getType(); - Type.timevals = new SharedAll.Type( - "two timevals", - ctypes.ArrayType(Type.timeval.implementation, 2) - ); - } - - // Types fsblkcnt_t and fsfilcnt_t, used by structure |statvfs| - Type.fsblkcnt_t = Type.uintn_t(Const.OSFILE_SIZEOF_FSBLKCNT_T).withName( - "fsblkcnt_t" - ); - // There is no guarantee of the size or order of members in sys-header structs - // It mostly is "unsigned long", but can be "unsigned int" as well. - // So it has its own "type". - // NOTE: This is still only partially correct, as signedness is also not guaranteed, - // so assuming an unsigned int might still be wrong here. - // But unsigned seems to have worked all those years, even though its signed - // on various platforms. - Type.statvfs_f_frsize = Type.uintn_t( - Const.OSFILE_SIZEOF_STATVFS_F_FRSIZE - ).withName("statvfs_f_rsize"); - - // Structure |statvfs| - // Use an hollow structure - { - let statvfs = new SharedAll.HollowStructure( - "statvfs", - Const.OSFILE_SIZEOF_STATVFS - ); - - statvfs.add_field_at( - Const.OSFILE_OFFSETOF_STATVFS_F_FRSIZE, - "f_frsize", - Type.statvfs_f_frsize.implementation - ); - statvfs.add_field_at( - Const.OSFILE_OFFSETOF_STATVFS_F_BAVAIL, - "f_bavail", - Type.fsblkcnt_t.implementation - ); - - Type.statvfs = statvfs.getType(); - } - - // Declare libc functions as functions of |OS.Unix.File| - - // Finalizer-related functions - libc.declareLazy( - SysFile, - "_close", - "close", - ctypes.default_abi, - /* return */ ctypes.int, - ctypes.int - ); - - SysFile.close = function close(fd) { - // Detach the finalizer and call |_close|. - return fd.dispose(); - }; - - libc.declareLazy( - SysFile, - "_close_dir", - "closedir", - ctypes.default_abi, - /* return */ ctypes.int, - Type.DIR.in_ptr.implementation - ); - - SysFile.closedir = function closedir(fd) { - // Detach the finalizer and call |_close_dir|. - return fd.dispose(); - }; - - { - // Symbol free() is special. - // We override the definition of free() on several platforms. - let default_lib = new SharedAll.Library("default_lib", "a.out"); - - // On platforms for which we override free(), nspr defines - // a special library name "a.out" that will resolve to the - // correct implementation free(). - // If it turns out we don't have an a.out library or a.out - // doesn't contain free, use the ordinary libc free. - - default_lib.declareLazyWithFallback( - libc, - SysFile, - "free", - "free", - ctypes.default_abi, - /* return*/ ctypes.void_t, - ctypes.voidptr_t - ); - } - - // Other functions - libc.declareLazyFFI( - SysFile, - "access", - "access", - ctypes.default_abi, - /* return*/ Type.negativeone_or_nothing, - Type.path, - Type.int - ); - - libc.declareLazyFFI( - SysFile, - "chmod", - "chmod", - ctypes.default_abi, - /* return*/ Type.negativeone_or_nothing, - Type.path, - Type.mode_t - ); - - libc.declareLazyFFI( - SysFile, - "chown", - "chown", - ctypes.default_abi, - /* return*/ Type.negativeone_or_nothing, - Type.path, - Type.uid_t, - Type.gid_t - ); - - libc.declareLazyFFI( - SysFile, - "copyfile", - "copyfile", - ctypes.default_abi, - /* return*/ Type.negativeone_or_nothing, - /* source*/ Type.path, - Type.path, - Type.void_t.in_ptr, - Type.uint32_t - ); - - libc.declareLazyFFI( - SysFile, - "dup", - "dup", - ctypes.default_abi, - /* return*/ Type.negativeone_or_fd, - Type.fd - ); - - if ("OSFILE_SIZEOF_DIR" in Const) { - // On platforms for which |dirfd| is a macro - SysFile.dirfd = function dirfd(DIRp) { - return Type.DIR.in_ptr.implementation(DIRp).contents.dd_fd; - }; - } else { - // On platforms for which |dirfd| is a function - libc.declareLazyFFI( - SysFile, - "dirfd", - "dirfd", - ctypes.default_abi, - /* return*/ Type.negativeone_or_fd, - Type.DIR.in_ptr - ); - } - - libc.declareLazyFFI( - SysFile, - "chdir", - "chdir", - ctypes.default_abi, - /* return*/ Type.negativeone_or_nothing, - Type.path - ); - - libc.declareLazyFFI( - SysFile, - "fchdir", - "fchdir", - ctypes.default_abi, - /* return*/ Type.negativeone_or_nothing, - Type.fd - ); - - libc.declareLazyFFI( - SysFile, - "fchmod", - "fchmod", - ctypes.default_abi, - /* return*/ Type.negativeone_or_nothing, - Type.fd, - Type.mode_t - ); - - libc.declareLazyFFI( - SysFile, - "fchown", - "fchown", - ctypes.default_abi, - /* return*/ Type.negativeone_or_nothing, - Type.fd, - Type.uid_t, - Type.gid_t - ); - - libc.declareLazyFFI( - SysFile, - "fsync", - "fsync", - ctypes.default_abi, - /* return*/ Type.negativeone_or_nothing, - Type.fd - ); - - libc.declareLazyFFI( - SysFile, - "getcwd", - "getcwd", - ctypes.default_abi, - /* return*/ Type.out_path, - Type.out_path, - Type.size_t - ); - - libc.declareLazyFFI( - SysFile, - "getwd", - "getwd", - ctypes.default_abi, - /* return*/ Type.out_path, - Type.out_path - ); - - // Two variants of |getwd| which allocate the memory - // dynamically. - - // Linux/Android version - libc.declareLazyFFI( - SysFile, - "get_current_dir_name", - "get_current_dir_name", - ctypes.default_abi, - /* return*/ Type.out_path.releaseWithLazy(() => SysFile.free) - ); - - // MacOS/BSD version (will return NULL on Linux/Android) - libc.declareLazyFFI( - SysFile, - "getwd_auto", - "getwd", - ctypes.default_abi, - /* return*/ Type.out_path.releaseWithLazy(() => SysFile.free), - Type.void_t.out_ptr - ); - - if (OS.Constants.Sys.Name == "Darwin") { - // At the time of writing we only need this on MacOS. If we generalize - // this, be sure to do so with the other xattr functions also. - libc.declareLazyFFI( - SysFile, - "getxattr", - "getxattr", - ctypes.default_abi, - /* return*/ Type.int, - Type.path, - Type.cstring, - Type.void_t.out_ptr, - Type.size_t, - Type.uint32_t, - Type.int - ); - } - - libc.declareLazyFFI( - SysFile, - "fdatasync", - "fdatasync", - ctypes.default_abi, - /* return*/ Type.negativeone_or_nothing, - Type.fd - ); // Note: MacOS/BSD-specific - - libc.declareLazyFFI( - SysFile, - "ftruncate", - "ftruncate", - ctypes.default_abi, - /* return*/ Type.negativeone_or_nothing, - Type.fd, - /* length*/ Type.off_t - ); - - libc.declareLazyFFI( - SysFile, - "lchown", - "lchown", - ctypes.default_abi, - /* return*/ Type.negativeone_or_nothing, - Type.path, - Type.uid_t, - Type.gid_t - ); - - libc.declareLazyFFI( - SysFile, - "link", - "link", - ctypes.default_abi, - /* return*/ Type.negativeone_or_nothing, - /* source*/ Type.path, - Type.path - ); - - libc.declareLazyFFI( - SysFile, - "lseek", - "lseek", - ctypes.default_abi, - /* return*/ Type.off_t, - Type.fd, - /* offset*/ Type.off_t, - /* whence*/ Type.int - ); - - libc.declareLazyFFI( - SysFile, - "mkdir", - "mkdir", - ctypes.default_abi, - /* return*/ Type.int, - /* path*/ Type.path, - /* mode*/ Type.int - ); - - libc.declareLazyFFI( - SysFile, - "mkstemp", - "mkstemp", - ctypes.default_abi, - Type.fd, - /* template*/ Type.out_path - ); - - libc.declareLazyFFI( - SysFile, - "open", - "open", - ctypes.default_abi, - /* return*/ Type.negativeone_or_fd, - Type.path, - /* oflags*/ Type.int, - "..." - ); - - if (OS.Constants.Sys.Name == "NetBSD") { - libc.declareLazyFFI( - SysFile, - "opendir", - "__opendir30", - ctypes.default_abi, - /* return*/ Type.null_or_DIR_ptr, - Type.path - ); - } else { - libc.declareLazyFFI( - SysFile, - "opendir", - "opendir", - ctypes.default_abi, - /* return*/ Type.null_or_DIR_ptr, - Type.path - ); - } - - libc.declareLazyFFI( - SysFile, - "pread", - "pread", - ctypes.default_abi, - /* return*/ Type.negativeone_or_ssize_t, - Type.fd, - Type.void_t.out_ptr, - /* nbytes*/ Type.size_t, - /* offset*/ Type.off_t - ); - - libc.declareLazyFFI( - SysFile, - "pwrite", - "pwrite", - ctypes.default_abi, - /* return*/ Type.negativeone_or_ssize_t, - Type.fd, - Type.void_t.in_ptr, - /* nbytes*/ Type.size_t, - /* offset*/ Type.off_t - ); - - libc.declareLazyFFI( - SysFile, - "read", - "read", - ctypes.default_abi, - /* return*/ Type.negativeone_or_ssize_t, - Type.fd, - Type.void_t.out_ptr, - /* nbytes*/ Type.size_t - ); - - libc.declareLazyFFI( - SysFile, - "posix_fadvise", - "posix_fadvise", - ctypes.default_abi, - /* return*/ Type.int, - Type.fd, - /* offset*/ Type.off_t, - Type.off_t, - /* advise*/ Type.int - ); - - if (Const._DARWIN_INODE64_SYMBOLS) { - // Special case for MacOS X 10.5+ - // Symbol name "readdir" still exists but is used for a - // deprecated function that does not match the - // constants of |Const|. - libc.declareLazyFFI( - SysFile, - "readdir", - "readdir$INODE64", - ctypes.default_abi, - /* return*/ Type.null_or_dirent_ptr, - Type.DIR.in_ptr - ); // For MacOS X - } else if (OS.Constants.Sys.Name == "NetBSD") { - libc.declareLazyFFI( - SysFile, - "readdir", - "__readdir30", - ctypes.default_abi, - /* return*/ Type.null_or_dirent_ptr, - Type.DIR.in_ptr - ); // Other Unices - } else { - libc.declareLazyFFI( - SysFile, - "readdir", - "readdir", - ctypes.default_abi, - /* return*/ Type.null_or_dirent_ptr, - Type.DIR.in_ptr - ); // Other Unices - } - - if (OS.Constants.Sys.Name == "Darwin") { - // At the time of writing we only need this on MacOS. If we generalize - // this, be sure to do so with the other xattr functions also. - libc.declareLazyFFI( - SysFile, - "removexattr", - "removexattr", - ctypes.default_abi, - /* return*/ Type.negativeone_or_nothing, - Type.path, - Type.cstring, - Type.int - ); - } - - libc.declareLazyFFI( - SysFile, - "rename", - "rename", - ctypes.default_abi, - /* return*/ Type.negativeone_or_nothing, - Type.path, - Type.path - ); - - libc.declareLazyFFI( - SysFile, - "rmdir", - "rmdir", - ctypes.default_abi, - /* return*/ Type.int, - Type.path - ); - - if (OS.Constants.Sys.Name == "Darwin") { - // At the time of writing we only need this on MacOS. If we generalize - // this, be sure to do so with the other xattr functions also. - libc.declareLazyFFI( - SysFile, - "setxattr", - "setxattr", - ctypes.default_abi, - /* return*/ Type.negativeone_or_nothing, - Type.path, - Type.cstring, - Type.void_t.in_ptr, - Type.size_t, - Type.uint32_t, - Type.int - ); - } - - libc.declareLazyFFI( - SysFile, - "splice", - "splice", - ctypes.default_abi, - /* return*/ Type.long, - Type.fd, - /* off_in*/ Type.off_t.in_ptr, - /* fd_out*/ Type.fd, - /* off_out*/ Type.off_t.in_ptr, - Type.size_t, - Type.unsigned_int - ); // Linux/Android-specific - - libc.declareLazyFFI( - SysFile, - "statfs", - "statfs", - ctypes.default_abi, - /* return*/ Type.negativeone_or_nothing, - Type.path, - Type.statvfs.out_ptr - ); // Android,B2G - - libc.declareLazyFFI( - SysFile, - "statvfs", - "statvfs", - ctypes.default_abi, - /* return*/ Type.negativeone_or_nothing, - Type.path, - Type.statvfs.out_ptr - ); // Other platforms - - libc.declareLazyFFI( - SysFile, - "symlink", - "symlink", - ctypes.default_abi, - /* return*/ Type.negativeone_or_nothing, - /* source*/ Type.path, - Type.path - ); - - libc.declareLazyFFI( - SysFile, - "truncate", - "truncate", - ctypes.default_abi, - /* return*/ Type.negativeone_or_nothing, - Type.path, - /* length*/ Type.off_t - ); - - libc.declareLazyFFI( - SysFile, - "unlink", - "unlink", - ctypes.default_abi, - /* return */ Type.negativeone_or_nothing, - /* path */ Type.path - ); - - libc.declareLazyFFI( - SysFile, - "write", - "write", - ctypes.default_abi, - /* return */ Type.negativeone_or_ssize_t, - /* fd */ Type.fd, - /* buf */ Type.void_t.in_ptr, - /* nbytes */ Type.size_t - ); - - // Weird cases that require special treatment - - // OSes use a variety of hacks to differentiate between - // 32-bits and 64-bits versions of |stat|, |lstat|, |fstat|. - if (Const._DARWIN_INODE64_SYMBOLS) { - // MacOS X 64-bits - libc.declareLazyFFI( - SysFile, - "stat", - "stat$INODE64", - ctypes.default_abi, - /* return */ Type.negativeone_or_nothing, - /* path */ Type.path, - /* buf */ Type.stat.out_ptr - ); - libc.declareLazyFFI( - SysFile, - "lstat", - "lstat$INODE64", - ctypes.default_abi, - /* return */ Type.negativeone_or_nothing, - /* path */ Type.path, - /* buf */ Type.stat.out_ptr - ); - libc.declareLazyFFI( - SysFile, - "fstat", - "fstat$INODE64", - ctypes.default_abi, - /* return */ Type.negativeone_or_nothing, - /* path */ Type.fd, - /* buf */ Type.stat.out_ptr - ); - } else if (Const._STAT_VER != undefined) { - const ver = Const._STAT_VER; - let xstat_name, lxstat_name, fxstat_name; - if (OS.Constants.Sys.Name == "SunOS") { - // Solaris - xstat_name = "_xstat"; - lxstat_name = "_lxstat"; - fxstat_name = "_fxstat"; - } else { - // Linux, all widths - xstat_name = "__xstat"; - lxstat_name = "__lxstat"; - fxstat_name = "__fxstat"; - } - - let Stat = {}; - libc.declareLazyFFI( - Stat, - "xstat", - xstat_name, - ctypes.default_abi, - /* return */ Type.negativeone_or_nothing, - /* _stat_ver */ Type.int, - /* path */ Type.path, - /* buf */ Type.stat.out_ptr - ); - libc.declareLazyFFI( - Stat, - "lxstat", - lxstat_name, - ctypes.default_abi, - /* return */ Type.negativeone_or_nothing, - /* _stat_ver */ Type.int, - /* path */ Type.path, - /* buf */ Type.stat.out_ptr - ); - libc.declareLazyFFI( - Stat, - "fxstat", - fxstat_name, - ctypes.default_abi, - /* return */ Type.negativeone_or_nothing, - /* _stat_ver */ Type.int, - /* fd */ Type.fd, - /* buf */ Type.stat.out_ptr - ); - - SysFile.stat = function stat(path, buf) { - return Stat.xstat(ver, path, buf); - }; - - SysFile.lstat = function lstat(path, buf) { - return Stat.lxstat(ver, path, buf); - }; - - SysFile.fstat = function fstat(fd, buf) { - return Stat.fxstat(ver, fd, buf); - }; - } else if (OS.Constants.Sys.Name == "NetBSD") { - // NetBSD 5.0 and newer - libc.declareLazyFFI( - SysFile, - "stat", - "__stat50", - ctypes.default_abi, - /* return */ Type.negativeone_or_nothing, - /* path */ Type.path, - /* buf */ Type.stat.out_ptr - ); - libc.declareLazyFFI( - SysFile, - "lstat", - "__lstat50", - ctypes.default_abi, - /* return */ Type.negativeone_or_nothing, - /* path */ Type.path, - /* buf */ Type.stat.out_ptr - ); - libc.declareLazyFFI( - SysFile, - "fstat", - "__fstat50", - ctypes.default_abi, - /* return */ Type.negativeone_or_nothing, - /* fd */ Type.fd, - /* buf */ Type.stat.out_ptr - ); - } else { - // Mac OS X 32-bits, other Unix - libc.declareLazyFFI( - SysFile, - "stat", - "stat", - ctypes.default_abi, - /* return */ Type.negativeone_or_nothing, - /* path */ Type.path, - /* buf */ Type.stat.out_ptr - ); - libc.declareLazyFFI( - SysFile, - "lstat", - "lstat", - ctypes.default_abi, - /* return */ Type.negativeone_or_nothing, - /* path */ Type.path, - /* buf */ Type.stat.out_ptr - ); - libc.declareLazyFFI( - SysFile, - "fstat", - "fstat", - ctypes.default_abi, - /* return */ Type.negativeone_or_nothing, - /* fd */ Type.fd, - /* buf */ Type.stat.out_ptr - ); - } - - // We cannot make a C array of CDataFinalizer, so - // pipe cannot be directly defined as a C function. - - let Pipe = {}; - libc.declareLazyFFI( - Pipe, - "_pipe", - "pipe", - ctypes.default_abi, - /* return */ Type.negativeone_or_nothing, - /* fds */ new SharedAll.Type( - "two file descriptors", - ctypes.ArrayType(ctypes.int, 2) - ) - ); - - // A shared per-thread buffer used to communicate with |pipe| - let _pipebuf = new (ctypes.ArrayType(ctypes.int, 2))(); - - SysFile.pipe = function pipe(array) { - let result = Pipe._pipe(_pipebuf); - if (result == -1) { - return result; - } - array[0] = ctypes.CDataFinalizer(_pipebuf[0], SysFile._close); - array[1] = ctypes.CDataFinalizer(_pipebuf[1], SysFile._close); - return result; - }; - - if (OS.Constants.Sys.Name == "NetBSD") { - libc.declareLazyFFI( - SysFile, - "utimes", - "__utimes50", - ctypes.default_abi, - /* return */ Type.negativeone_or_nothing, - /* path */ Type.path, - /* timeval[2] */ Type.timevals.out_ptr - ); - } else { - libc.declareLazyFFI( - SysFile, - "utimes", - "utimes", - ctypes.default_abi, - /* return */ Type.negativeone_or_nothing, - /* path */ Type.path, - /* timeval[2] */ Type.timevals.out_ptr - ); - } - if (OS.Constants.Sys.Name == "NetBSD") { - libc.declareLazyFFI( - SysFile, - "futimes", - "__futimes50", - ctypes.default_abi, - /* return */ Type.negativeone_or_nothing, - /* fd */ Type.fd, - /* timeval[2] */ Type.timevals.out_ptr - ); - } else { - libc.declareLazyFFI( - SysFile, - "futimes", - "futimes", - ctypes.default_abi, - /* return */ Type.negativeone_or_nothing, - /* fd */ Type.fd, - /* timeval[2] */ Type.timevals.out_ptr - ); - } - }; - - exports.OS.Unix = { - File: { - _init: init, - }, - }; - })(this); -}
deleted file mode 100644 --- a/toolkit/components/osfile/modules/osfile_unix_front.js +++ /dev/null @@ -1,1243 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Synchronous front-end for the JavaScript OS.File library. - * Unix implementation. - * - * This front-end is meant to be imported by a worker thread. - */ - -/* eslint-env mozilla/chrome-worker, node */ -/* global OS */ - -// eslint-disable-next-line no-lone-blocks -{ - if (typeof Components != "undefined") { - // We do not wish osfile_unix_front.js to be used directly as a main thread - // module yet. - - throw new Error( - "osfile_unix_front.js cannot be used from the main thread yet" - ); - } - (function(exports) { - "use strict"; - - // exports.OS.Unix is created by osfile_unix_back.js - if (exports.OS && exports.OS.File) { - return; // Avoid double-initialization - } - - let SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm"); - let Path = require("resource://gre/modules/osfile/ospath.jsm"); - let SysAll = require("resource://gre/modules/osfile/osfile_unix_allthreads.jsm"); - exports.OS.Unix.File._init(); - SharedAll.LOG.bind(SharedAll, "Unix front-end"); - let Const = SharedAll.Constants.libc; - let UnixFile = exports.OS.Unix.File; - let Type = UnixFile.Type; - - /** - * Representation of a file. - * - * You generally do not need to call this constructor yourself. Rather, - * to open a file, use function |OS.File.open|. - * - * @param fd A OS-specific file descriptor. - * @param {string} path File path of the file handle, used for error-reporting. - * @constructor - */ - let File = function File(fd, path) { - exports.OS.Shared.AbstractFile.call(this, fd, path); - this._closeResult = null; - }; - File.prototype = Object.create(exports.OS.Shared.AbstractFile.prototype); - - /** - * Close the file. - * - * This method has no effect if the file is already closed. However, - * if the first call to |close| has thrown an error, further calls - * will throw the same error. - * - * @throws File.Error If closing the file revealed an error that could - * not be reported earlier. - */ - File.prototype.close = function close() { - if (this._fd) { - let fd = this._fd; - this._fd = null; - // Call |close(fd)|, detach finalizer if any - // (|fd| may not be a CDataFinalizer if it has been - // instantiated from a controller thread). - let result = UnixFile._close(fd); - if (typeof fd == "object" && "forget" in fd) { - fd.forget(); - } - if (result == -1) { - this._closeResult = new File.Error("close", ctypes.errno, this._path); - } - } - if (this._closeResult) { - throw this._closeResult; - } - }; - - /** - * Read some bytes from a file. - * - * @param {C pointer} buffer A buffer for holding the data - * once it is read. - * @param {number} nbytes The number of bytes to read. It must not - * exceed the size of |buffer| in bytes but it may exceed the number - * of bytes unread in the file. - * @param {*=} options Additional options for reading. Ignored in - * this implementation. - * - * @return {number} The number of bytes effectively read. If zero, - * the end of the file has been reached. - * @throws {OS.File.Error} In case of I/O error. - */ - File.prototype._read = function _read(buffer, nbytes, options = {}) { - // Populate the page cache with data from a file so the subsequent reads - // from that file will not block on disk I/O. - if ( - typeof UnixFile.posix_fadvise === "function" && - (options.sequential || !("sequential" in options)) - ) { - UnixFile.posix_fadvise( - this.fd, - 0, - nbytes, - OS.Constants.libc.POSIX_FADV_SEQUENTIAL - ); - } - return throw_on_negative( - "read", - UnixFile.read(this.fd, buffer, nbytes), - this._path - ); - }; - - /** - * Write some bytes to a file. - * - * @param {Typed array} buffer A buffer holding the data that must be - * written. - * @param {number} nbytes The number of bytes to write. It must not - * exceed the size of |buffer| in bytes. - * @param {*=} options Additional options for writing. Ignored in - * this implementation. - * - * @return {number} The number of bytes effectively written. - * @throws {OS.File.Error} In case of I/O error. - */ - File.prototype._write = function _write(buffer, nbytes, options = {}) { - return throw_on_negative( - "write", - UnixFile.write(this.fd, buffer, nbytes), - this._path - ); - }; - - /** - * Return the current position in the file. - */ - File.prototype.getPosition = function getPosition(pos) { - return this.setPosition(0, File.POS_CURRENT); - }; - - /** - * Change the current position in the file. - * - * @param {number} pos The new position. Whether this position - * is considered from the current position, from the start of - * the file or from the end of the file is determined by - * argument |whence|. Note that |pos| may exceed the length of - * the file. - * @param {number=} whence The reference position. If omitted - * or |OS.File.POS_START|, |pos| is relative to the start of the - * file. If |OS.File.POS_CURRENT|, |pos| is relative to the - * current position in the file. If |OS.File.POS_END|, |pos| is - * relative to the end of the file. - * - * @return The new position in the file. - */ - File.prototype.setPosition = function setPosition(pos, whence) { - if (whence === undefined) { - whence = Const.SEEK_SET; - } - return throw_on_negative( - "setPosition", - UnixFile.lseek(this.fd, pos, whence), - this._path - ); - }; - - /** - * Fetch the information on the file. - * - * @return File.Info The information on |this| file. - */ - File.prototype.stat = function stat() { - throw_on_negative( - "stat", - UnixFile.fstat(this.fd, gStatDataPtr), - this._path - ); - return new File.Info(gStatData, this._path); - }; - - /** - * Set the file's access permissions. - * - * This operation is likely to fail if applied to a file that was - * not created by the currently running program (more precisely, - * if it was created by a program running under a different OS-level - * user account). It may also fail, or silently do nothing, if the - * filesystem containing the file does not support access permissions. - * - * @param {*=} options Object specifying the requested permissions: - * - * - {number} unixMode The POSIX file mode to set on the file. If omitted, - * the POSIX file mode is reset to the default used by |OS.file.open|. If - * specified, the permissions will respect the process umask as if they - * had been specified as arguments of |OS.File.open|, unless the - * |unixHonorUmask| parameter tells otherwise. - * - {bool} unixHonorUmask If omitted or true, any |unixMode| value is - * modified by the process umask, as |OS.File.open| would have done. If - * false, the exact value of |unixMode| will be applied. - */ - File.prototype.setPermissions = function setPermissions(options = {}) { - throw_on_negative( - "setPermissions", - UnixFile.fchmod(this.fd, unixMode(options)), - this._path - ); - }; - - /** - * Set the last access and modification date of the file. - * The time stamp resolution is 1 second at best, but might be worse - * depending on the platform. - * - * WARNING: This method is not implemented on Android/B2G. On Android/B2G, - * you should use File.setDates instead. - * - * @param {Date,number=} accessDate The last access date. If numeric, - * milliseconds since epoch. If omitted or null, then the current date - * will be used. - * @param {Date,number=} modificationDate The last modification date. If - * numeric, milliseconds since epoch. If omitted or null, then the current - * date will be used. - * - * @throws {TypeError} In case of invalid parameters. - * @throws {OS.File.Error} In case of I/O error. - */ - if (SharedAll.Constants.Sys.Name != "Android") { - File.prototype.setDates = function(accessDate, modificationDate) { - let { /* value, */ ptr } = datesToTimevals( - accessDate, - modificationDate - ); - throw_on_negative( - "setDates", - UnixFile.futimes(this.fd, ptr), - this._path - ); - }; - } - - /** - * Flushes the file's buffers and causes all buffered data - * to be written. - * Disk flushes are very expensive and therefore should be used carefully, - * sparingly and only in scenarios where it is vital that data survives - * system crashes. Even though the function will be executed off the - * main-thread, it might still affect the overall performance of any - * running application. - * - * @throws {OS.File.Error} In case of I/O error. - */ - File.prototype.flush = function flush() { - throw_on_negative("flush", UnixFile.fsync(this.fd), this._path); - }; - - // The default unix mode for opening (0600) - const DEFAULT_UNIX_MODE = 384; - - /** - * Open a file - * - * @param {string} path The path to the file. - * @param {*=} mode The opening mode for the file, as - * an object that may contain the following fields: - * - * - {bool} truncate If |true|, the file will be opened - * for writing. If the file does not exist, it will be - * created. If the file exists, its contents will be - * erased. Cannot be specified with |create|. - * - {bool} create If |true|, the file will be opened - * for writing. If the file exists, this function fails. - * If the file does not exist, it will be created. Cannot - * be specified with |truncate| or |existing|. - * - {bool} existing. If the file does not exist, this function - * fails. Cannot be specified with |create|. - * - {bool} read If |true|, the file will be opened for - * reading. The file may also be opened for writing, depending - * on the other fields of |mode|. - * - {bool} write If |true|, the file will be opened for - * writing. The file may also be opened for reading, depending - * on the other fields of |mode|. - * - {bool} append If |true|, the file will be opened for appending, - * meaning the equivalent of |.setPosition(0, POS_END)| is executed - * before each write. The default is |true|, i.e. opening a file for - * appending. Specify |append: false| to open the file in regular mode. - * - * If neither |truncate|, |create| or |write| is specified, the file - * is opened for reading. - * - * Note that |false|, |null| or |undefined| flags are simply ignored. - * - * @param {*=} options Additional options for file opening. This - * implementation interprets the following fields: - * - * - {number} unixFlags If specified, file opening flags, as - * per libc function |open|. Replaces |mode|. - * - {number} unixMode If specified, a file creation mode, - * as per libc function |open|. If unspecified, files are - * created with a default mode of 0600 (file is private to the - * user, the user can read and write). - * - * @return {File} A file object. - * @throws {OS.File.Error} If the file could not be opened. - */ - File.open = function Unix_open(path, mode, options = {}) { - // We don't need to filter for the umask because "open" does this for us. - let omode = - options.unixMode !== undefined ? options.unixMode : DEFAULT_UNIX_MODE; - let flags; - if (options.unixFlags !== undefined) { - flags = options.unixFlags; - } else { - mode = OS.Shared.AbstractFile.normalizeOpenMode(mode); - // Handle read/write - if (!mode.write) { - flags = Const.O_RDONLY; - } else if (mode.read) { - flags = Const.O_RDWR; - } else { - flags = Const.O_WRONLY; - } - // Finally, handle create/existing/trunc - if (mode.trunc) { - if (mode.existing) { - flags |= Const.O_TRUNC; - } else { - flags |= Const.O_CREAT | Const.O_TRUNC; - } - } else if (mode.create) { - flags |= Const.O_CREAT | Const.O_EXCL; - } else if (mode.read && !mode.write) { - // flags are sufficient - } else if (!mode.existing) { - flags |= Const.O_CREAT; - } - if (mode.append) { - flags |= Const.O_APPEND; - } - } - return error_or_file(UnixFile.open(path, flags, ctypes.int(omode)), path); - }; - - /** - * Checks if a file exists - * - * @param {string} path The path to the file. - * - * @return {bool} true if the file exists, false otherwise. - */ - File.exists = function Unix_exists(path) { - if (UnixFile.access(path, Const.F_OK) == -1) { - return false; - } - return true; - }; - - /** - * Remove an existing file. - * - * @param {string} path The name of the file. - * @param {*=} options Additional options. - * - {bool} ignoreAbsent If |false|, throw an error if the file does - * not exist. |true| by default. - * - * @throws {OS.File.Error} In case of I/O error. - */ - File.remove = function remove(path, options = {}) { - let result = UnixFile.unlink(path); - if (result == -1) { - if ( - (!("ignoreAbsent" in options) || options.ignoreAbsent) && - ctypes.errno == Const.ENOENT - ) { - return; - } - throw new File.Error("remove", ctypes.errno, path); - } - }; - - /** - * Remove an empty directory. - * - * @param {string} path The name of the directory to remove. - * @param {*=} options Additional options. - * - {bool} ignoreAbsent If |false|, throw an error if the directory - * does not exist. |true| by default - */ - File.removeEmptyDir = function removeEmptyDir(path, options = {}) { - let result = UnixFile.rmdir(path); - if (result == -1) { - if ( - (!("ignoreAbsent" in options) || options.ignoreAbsent) && - ctypes.errno == Const.ENOENT - ) { - return; - } - throw new File.Error("removeEmptyDir", ctypes.errno, path); - } - }; - - /** - * Default mode for opening directories. - */ - const DEFAULT_UNIX_MODE_DIR = Const.S_IRWXU; - - /** - * Create a directory. - * - * @param {string} path The name of the directory. - * @param {*=} options Additional options. This - * implementation interprets the following fields: - * - * - {number} unixMode If specified, a file creation mode, - * as per libc function |mkdir|. If unspecified, dirs are - * created with a default mode of 0700 (dir is private to - * the user, the user can read, write and execute). - * - {bool} ignoreExisting If |false|, throw error if the directory - * already exists. |true| by default - * - {string} from If specified, the call to |makeDir| creates all the - * ancestors of |path| that are descendants of |from|. Note that |from| - * and its existing descendants must be user-writeable and that |path| - * must be a descendant of |from|. - * Example: - * makeDir(Path.join(profileDir, "foo", "bar"), { from: profileDir }); - * creates directories profileDir/foo, profileDir/foo/bar - */ - File._makeDir = function makeDir(path, options = {}) { - let omode = - options.unixMode !== undefined - ? options.unixMode - : DEFAULT_UNIX_MODE_DIR; - let result = UnixFile.mkdir(path, omode); - if (result == -1) { - if ( - (!("ignoreExisting" in options) || options.ignoreExisting) && - (ctypes.errno == Const.EEXIST || ctypes.errno == Const.EISDIR) - ) { - return; - } - throw new File.Error("makeDir", ctypes.errno, path); - } - }; - - /** - * Copy a file to a destination. - * - * @param {string} sourcePath The platform-specific path at which - * the file may currently be found. - * @param {string} destPath The platform-specific path at which the - * file should be copied. - * @param {*=} options An object which may contain the following fields: - * - * @option {bool} noOverwrite - If set, this function will fail if - * a file already exists at |destPath|. Otherwise, if this file exists, - * it will be erased silently. - * - * @throws {OS.File.Error} In case of any error. - * - * General note: The behavior of this function is defined only when - * it is called on a single file. If it is called on a directory, the - * behavior is undefined and may not be the same across all platforms. - * - * General note: The behavior of this function with respect to metadata - * is unspecified. Metadata may or may not be copied with the file. The - * behavior may not be the same across all platforms. - */ - File.copy = null; - - /** - * Move a file to a destination. - * - * @param {string} sourcePath The platform-specific path at which - * the file may currently be found. - * @param {string} destPath The platform-specific path at which the - * file should be moved. - * @param {*=} options An object which may contain the following fields: - * - * @option {bool} noOverwrite - If set, this function will fail if - * a file already exists at |destPath|. Otherwise, if this file exists, - * it will be erased silently. - * @option {bool} noCopy - If set, this function will fail if the - * operation is more sophisticated than a simple renaming, i.e. if - * |sourcePath| and |destPath| are not situated on the same device. - * - * @throws {OS.File.Error} In case of any error. - * - * General note: The behavior of this function is defined only when - * it is called on a single file. If it is called on a directory, the - * behavior is undefined and may not be the same across all platforms. - * - * General note: The behavior of this function with respect to metadata - * is unspecified. Metadata may or may not be moved with the file. The - * behavior may not be the same across all platforms. - */ - File.move = null; - - if (UnixFile.copyfile) { - // This implementation uses |copyfile(3)|, from the BSD library. - // Adding copying of hierarchies and/or attributes is just a flag - // away. - File.copy = function copyfile(sourcePath, destPath, options = {}) { - let flags = Const.COPYFILE_DATA; - if (options.noOverwrite) { - flags |= Const.COPYFILE_EXCL; - } - throw_on_negative( - "copy", - UnixFile.copyfile(sourcePath, destPath, null, flags), - sourcePath - ); - }; - } else { - // If the OS does not implement file copying for us, we need to - // implement it ourselves. For this purpose, we need to define - // a pumping function. - - /** - * Copy bytes from one file to another one. - * - * @param {File} source The file containing the data to be copied. It - * should be opened for reading. - * @param {File} dest The file to which the data should be written. It - * should be opened for writing. - * @param {*=} options An object which may contain the following fields: - * - * @option {number} nbytes The maximal number of bytes to - * copy. If unspecified, copy everything from the current - * position. - * @option {number} bufSize A hint regarding the size of the - * buffer to use for copying. The implementation may decide to - * ignore this hint. - * @option {bool} unixUserland Will force the copy operation to be - * caried out in user land, instead of using optimized syscalls such - * as splice(2). - * - * @throws {OS.File.Error} In case of error. - */ - let pump; - - // A buffer used by |pump_userland| - let pump_buffer = null; - - // An implementation of |pump| using |read|/|write| - let pump_userland = function pump_userland(source, dest, options = {}) { - let bufSize = options.bufSize > 0 ? options.bufSize : 4096; - let nbytes = options.nbytes > 0 ? options.nbytes : Infinity; - if (!pump_buffer || pump_buffer.length < bufSize) { - pump_buffer = new (ctypes.ArrayType(ctypes.char))(bufSize); - } - let read = source._read.bind(source); - let write = dest._write.bind(dest); - // Perform actual copy - let total_read = 0; - while (true) { - let bytes_just_read = read(pump_buffer, bufSize); - if (bytes_just_read == 0) { - return total_read; - } - total_read += bytes_just_read; - let bytes_written = 0; - do { - bytes_written += write( - pump_buffer.addressOfElement(bytes_written), - bytes_just_read - bytes_written - ); - } while (bytes_written < bytes_just_read); - nbytes -= bytes_written; - if (nbytes <= 0) { - return total_read; - } - } - }; - - // Fortunately, under Linux, that pumping function can be optimized. - if (UnixFile.splice) { - const BUFSIZE = 1 << 17; - - // An implementation of |pump| using |splice| (for Linux/Android) - pump = function pump_splice(source, dest, options = {}) { - let nbytes = options.nbytes > 0 ? options.nbytes : Infinity; - let pipe = []; - throw_on_negative("pump", UnixFile.pipe(pipe)); - let pipe_read = pipe[0]; - let pipe_write = pipe[1]; - let source_fd = source.fd; - let dest_fd = dest.fd; - let total_read = 0; - let total_written = 0; - try { - while (true) { - let chunk_size = Math.min(nbytes, BUFSIZE); - let bytes_read = throw_on_negative( - "pump", - UnixFile.splice( - source_fd, - null, - pipe_write, - null, - chunk_size, - 0 - ) - ); - if (!bytes_read) { - break; - } - total_read += bytes_read; - let bytes_written = throw_on_negative( - "pump", - UnixFile.splice( - pipe_read, - null, - dest_fd, - null, - bytes_read, - bytes_read == chunk_size ? Const.SPLICE_F_MORE : 0 - ) - ); - if (!bytes_written) { - // This should never happen - throw new Error("Internal error: pipe disconnected"); - } - total_written += bytes_written; - nbytes -= bytes_read; - if (!nbytes) { - break; - } - } - return total_written; - } catch (x) { - if (x.unixErrno == Const.EINVAL) { - // We *might* be on a file system that does not support splice. - // Try again with a fallback pump. - if (total_read) { - source.setPosition(-total_read, File.POS_CURRENT); - } - if (total_written) { - dest.setPosition(-total_written, File.POS_CURRENT); - } - return pump_userland(source, dest, options); - } - throw x; - } finally { - pipe_read.dispose(); - pipe_write.dispose(); - } - }; - } else { - // Fallback implementation of pump for other Unix platforms. - pump = pump_userland; - } - - // Implement |copy| using |pump|. - // This implementation would require some work before being able to - // copy directories - File.copy = function copy(sourcePath, destPath, options = {}) { - let source, dest; - try { - source = File.open(sourcePath); - // Need to open the output file with |append:false|, or else |splice| - // won't work. - if (options.noOverwrite) { - dest = File.open(destPath, { create: true, append: false }); - } else { - dest = File.open(destPath, { trunc: true, append: false }); - } - if (options.unixUserland) { - pump_userland(source, dest, options); - } else { - pump(source, dest, options); - } - } catch (x) { - if (dest) { - dest.close(); - } - if (source) { - source.close(); - } - throw x; - } - }; - } // End of definition of copy - - // Implement |move| using |rename| (wherever possible) or |copy| - // (if files are on distinct devices). - File.move = function move(sourcePath, destPath, options = {}) { - // An implementation using |rename| whenever possible or - // |File.pump| when required, for other Unices. - // It can move directories on one file system, not - // across file systems - - // If necessary, fail if the destination file exists - if (options.noOverwrite) { - let fd = UnixFile.open(destPath, Const.O_RDONLY); - if (fd != -1) { - fd.dispose(); - // The file exists and we have access - throw new File.Error("move", Const.EEXIST, sourcePath); - } else if (ctypes.errno == Const.EACCESS) { - // The file exists and we don't have access - throw new File.Error("move", Const.EEXIST, sourcePath); - } - } - - // If we can, rename the file. - let result = UnixFile.rename(sourcePath, destPath); - if (result != -1) { - // Succeeded. - return; - } - - // In some cases, we cannot rename, e.g. because we're crossing - // devices. In such cases, if permitted, we'll need to copy then - // erase the original. - if (options.noCopy) { - throw new File.Error("move", ctypes.errno, sourcePath); - } - - File.copy(sourcePath, destPath, options); - // Note that we do not attempt to clean-up in case of copy error. - // I'm sure that there are edge cases in which this could end up - // removing an important file by accident. I'd rather leave - // a file lying around by error than removing a critical file. - - File.remove(sourcePath); - }; - - File.unixSymLink = function unixSymLink(sourcePath, destPath) { - throw_on_negative( - "symlink", - UnixFile.symlink(sourcePath, destPath), - sourcePath - ); - }; - - /** - * Iterate on one directory. - * - * This iterator will not enter subdirectories. - * - * @param {string} path The directory upon which to iterate. - * @param {*=} options Ignored in this implementation. - * - * @throws {File.Error} If |path| does not represent a directory or - * if the directory cannot be iterated. - * @constructor - */ - File.DirectoryIterator = function DirectoryIterator(path, options) { - exports.OS.Shared.AbstractFile.AbstractIterator.call(this); - this._path = path; - this._dir = UnixFile.opendir(this._path); - if (this._dir == null) { - let error = ctypes.errno; - if (error != Const.ENOENT) { - throw new File.Error("DirectoryIterator", error, path); - } - this._exists = false; - this._closed = true; - } else { - this._exists = true; - this._closed = false; - } - }; - File.DirectoryIterator.prototype = Object.create( - exports.OS.Shared.AbstractFile.AbstractIterator.prototype - ); - - /** - * Return the next entry in the directory, if any such entry is - * available. - * - * Skip special directories "." and "..". - * - * @return By definition of the iterator protocol, either - * `{value: {File.Entry}, done: false}` if there is an unvisited entry - * in the directory, or `{value: undefined, done: true}`, otherwise. - */ - File.DirectoryIterator.prototype.next = function next() { - if (!this._exists) { - throw File.Error.noSuchFile( - "DirectoryIterator.prototype.next", - this._path - ); - } - if (this._closed) { - return { value: undefined, done: true }; - } - for ( - let entry = UnixFile.readdir(this._dir); - entry != null && !entry.isNull(); - entry = UnixFile.readdir(this._dir) - ) { - let contents = entry.contents; - let name = contents.d_name.readString(); - if (name == "." || name == "..") { - continue; - } - - let isDir, isSymLink; - if ( - !("d_type" in contents) || - !("DT_UNKNOWN" in Const) || - contents.d_type == Const.DT_UNKNOWN - ) { - // File type information is not available in d_type. The cases are: - // 1. |dirent| doesn't have d_type on some platforms (e.g. Solaris). - // 2. DT_UNKNOWN and other DT_ constants are not defined. - // 3. d_type is set to unknown (e.g. not supported by the - // filesystem). - let path = Path.join(this._path, name); - throw_on_negative( - "lstat", - UnixFile.lstat(path, gStatDataPtr), - this._path - ); - isDir = (gStatData.st_mode & Const.S_IFMT) == Const.S_IFDIR; - isSymLink = (gStatData.st_mode & Const.S_IFMT) == Const.S_IFLNK; - } else { - isDir = contents.d_type == Const.DT_DIR; - isSymLink = contents.d_type == Const.DT_LNK; - } - - return { - value: new File.DirectoryIterator.Entry( - isDir, - isSymLink, - name, - this._path - ), - done: false, - }; - } - this.close(); - return { value: undefined, done: true }; - }; - - /** - * Close the iterator and recover all resources. - * You should call this once you have finished iterating on a directory. - */ - File.DirectoryIterator.prototype.close = function close() { - if (this._closed) { - return; - } - this._closed = true; - UnixFile.closedir(this._dir); - this._dir = null; - }; - - /** - * Determine whether the directory exists. - * - * @return {boolean} - */ - File.DirectoryIterator.prototype.exists = function exists() { - return this._exists; - }; - - /** - * Return directory as |File| - */ - File.DirectoryIterator.prototype.unixAsFile = function unixAsFile() { - if (!this._dir) { - throw File.Error.closed("unixAsFile", this._path); - } - return error_or_file(UnixFile.dirfd(this._dir), this._path); - }; - - /** - * An entry in a directory. - */ - File.DirectoryIterator.Entry = function Entry( - isDir, - isSymLink, - name, - parent - ) { - // Copy the relevant part of |unix_entry| to ensure that - // our data is not overwritten prematurely. - this._parent = parent; - let path = Path.join(this._parent, name); - - SysAll.AbstractEntry.call(this, isDir, isSymLink, name, path); - }; - File.DirectoryIterator.Entry.prototype = Object.create( - SysAll.AbstractEntry.prototype - ); - - /** - * Return a version of an instance of - * File.DirectoryIterator.Entry that can be sent from a worker - * thread to the main thread. Note that deserialization is - * asymmetric and returns an object with a different - * implementation. - */ - File.DirectoryIterator.Entry.toMsg = function toMsg(value) { - if (!(value instanceof File.DirectoryIterator.Entry)) { - throw new TypeError( - "parameter of " + - "File.DirectoryIterator.Entry.toMsg must be a " + - "File.DirectoryIterator.Entry" - ); - } - let serialized = {}; - for (let key in File.DirectoryIterator.Entry.prototype) { - serialized[key] = value[key]; - } - return serialized; - }; - - let gStatData = new Type.stat.implementation(); - let gStatDataPtr = gStatData.address(); - - let MODE_MASK = 4095; /* = 07777*/ - File.Info = function Info(stat, path) { - let isDir = (stat.st_mode & Const.S_IFMT) == Const.S_IFDIR; - let isSymLink = (stat.st_mode & Const.S_IFMT) == Const.S_IFLNK; - let size = Type.off_t.importFromC(stat.st_size); - - let lastAccessDate = new Date(stat.st_atime * 1000); - let lastModificationDate = new Date(stat.st_mtime * 1000); - let unixLastStatusChangeDate = new Date(stat.st_ctime * 1000); - - let unixOwner = Type.uid_t.importFromC(stat.st_uid); - let unixGroup = Type.gid_t.importFromC(stat.st_gid); - let unixMode = Type.mode_t.importFromC(stat.st_mode & MODE_MASK); - - SysAll.AbstractInfo.call( - this, - path, - isDir, - isSymLink, - size, - lastAccessDate, - lastModificationDate, - unixLastStatusChangeDate, - unixOwner, - unixGroup, - unixMode - ); - }; - File.Info.prototype = Object.create(SysAll.AbstractInfo.prototype); - - /** - * Return a version of an instance of File.Info that can be sent - * from a worker thread to the main thread. Note that deserialization - * is asymmetric and returns an object with a different implementation. - */ - File.Info.toMsg = function toMsg(stat) { - if (!(stat instanceof File.Info)) { - throw new TypeError("parameter of File.Info.toMsg must be a File.Info"); - } - let serialized = {}; - for (let key in File.Info.prototype) { - serialized[key] = stat[key]; - } - return serialized; - }; - - /** - * Fetch the information on a file. - * - * @param {string} path The full name of the file to open. - * @param {*=} options Additional options. In this implementation: - * - * - {bool} unixNoFollowingLinks If set and |true|, if |path| - * represents a symbolic link, the call will return the information - * of the link itself, rather than that of the target file. - * - * @return {File.Information} - */ - File.stat = function stat(path, options = {}) { - if (options.unixNoFollowingLinks) { - throw_on_negative("stat", UnixFile.lstat(path, gStatDataPtr), path); - } else { - throw_on_negative("stat", UnixFile.stat(path, gStatDataPtr), path); - } - return new File.Info(gStatData, path); - }; - - /** - * Set the file's access permissions. - * - * This operation is likely to fail if applied to a file that was - * not created by the currently running program (more precisely, - * if it was created by a program running under a different OS-level - * user account). It may also fail, or silently do nothing, if the - * filesystem containing the file does not support access permissions. - * - * @param {string} path The name of the file to reset the permissions of. - * @param {*=} options Object specifying the requested permissions: - * - * - {number} unixMode The POSIX file mode to set on the file. If omitted, - * the POSIX file mode is reset to the default used by |OS.file.open|. If - * specified, the permissions will respect the process umask as if they - * had been specified as arguments of |OS.File.open|, unless the - * |unixHonorUmask| parameter tells otherwise. - * - {bool} unixHonorUmask If omitted or true, any |unixMode| value is - * modified by the process umask, as |OS.File.open| would have done. If - * false, the exact value of |unixMode| will be applied. - */ - File.setPermissions = function setPermissions(path, options = {}) { - throw_on_negative( - "setPermissions", - UnixFile.chmod(path, unixMode(options)), - path - ); - }; - - /** - * Convert an access date and a modification date to an array - * of two |timeval|. - */ - function datesToTimevals(accessDate, modificationDate) { - accessDate = normalizeDate("File.setDates", accessDate); - modificationDate = normalizeDate("File.setDates", modificationDate); - - let timevals = new Type.timevals.implementation(); - let timevalsPtr = timevals.address(); - - // JavaScript date values are expressed in milliseconds since epoch. - // Split this up into second and microsecond components. - timevals[0].tv_sec = (accessDate / 1000) | 0; - timevals[0].tv_usec = ((accessDate % 1000) * 1000) | 0; - timevals[1].tv_sec = (modificationDate / 1000) | 0; - timevals[1].tv_usec = ((modificationDate % 1000) * 1000) | 0; - - return { value: timevals, ptr: timevalsPtr }; - } - - /** - * Set the last access and modification date of the file. - * The time stamp resolution is 1 second at best, but might be worse - * depending on the platform. - * - * @param {string} path The full name of the file to set the dates for. - * @param {Date,number=} accessDate The last access date. If numeric, - * milliseconds since epoch. If omitted or null, then the current date - * will be used. - * @param {Date,number=} modificationDate The last modification date. If - * numeric, milliseconds since epoch. If omitted or null, then the current - * date will be used. - * - * @throws {TypeError} In case of invalid paramters. - * @throws {OS.File.Error} In case of I/O error. - */ - File.setDates = function setDates(path, accessDate, modificationDate) { - let { /* value, */ ptr } = datesToTimevals(accessDate, modificationDate); - throw_on_negative("setDates", UnixFile.utimes(path, ptr), path); - }; - - File.read = exports.OS.Shared.AbstractFile.read; - File.writeAtomic = exports.OS.Shared.AbstractFile.writeAtomic; - File.openUnique = exports.OS.Shared.AbstractFile.openUnique; - File.makeDir = exports.OS.Shared.AbstractFile.makeDir; - - /** - * Remove an existing directory and its contents. - * - * @param {string} path The name of the directory. - * @param {*=} options Additional options. - * - {bool} ignoreAbsent If |false|, throw an error if the directory doesn't - * exist. |true| by default. - * - {boolean} ignorePermissions If |true|, remove the file even when lacking write - * permission. - * - * @throws {OS.File.Error} In case of I/O error, in particular if |path| is - * not a directory. - * - * Note: This function will remove a symlink even if it points a directory. - */ - File.removeDir = function(path, options = {}) { - let isSymLink; - try { - let info = File.stat(path, { unixNoFollowingLinks: true }); - isSymLink = info.isSymLink; - } catch (e) { - if ( - (!("ignoreAbsent" in options) || options.ignoreAbsent) && - ctypes.errno == Const.ENOENT - ) { - return; - } - throw e; - } - if (isSymLink) { - // A Unix symlink itself is not a directory even if it points - // a directory. - File.remove(path, options); - return; - } - exports.OS.Shared.AbstractFile.removeRecursive(path, options); - }; - - /** - * Get the current directory by getCurrentDirectory. - */ - File.getCurrentDirectory = function getCurrentDirectory() { - let path, buf; - if (UnixFile.get_current_dir_name) { - path = UnixFile.get_current_dir_name(); - } else if (UnixFile.getwd_auto) { - path = UnixFile.getwd_auto(null); - } else { - for (let length = Const.PATH_MAX; !path; length *= 2) { - buf = new (ctypes.char.array(length))(); - path = UnixFile.getcwd(buf, length); - } - } - throw_on_null("getCurrentDirectory", path); - return path.readString(); - }; - - /** - * Get/set the current directory. - */ - Object.defineProperty(File, "curDir", { - set(path) { - this.setCurrentDirectory(path); - }, - get() { - return this.getCurrentDirectory(); - }, - }); - - // Utility functions - - /** - * Turn the result of |open| into an Error or a File - * @param {number} maybe The result of the |open| operation that may - * represent either an error or a success. If -1, this function raises - * an error holding ctypes.errno, otherwise it returns the opened file. - * @param {string=} path The path of the file. - */ - function error_or_file(maybe, path) { - if (maybe == -1) { - throw new File.Error("open", ctypes.errno, path); - } - return new File(maybe, path); - } - - /** - * Utility function to sort errors represented as "-1" from successes. - * - * @param {string=} operation The name of the operation. If unspecified, - * the name of the caller function. - * @param {number} result The result of the operation that may - * represent either an error or a success. If -1, this function raises - * an error holding ctypes.errno, otherwise it returns |result|. - * @param {string=} path The path of the file. - */ - function throw_on_negative(operation, result, path) { - if (result < 0) { - throw new File.Error(operation, ctypes.errno, path); - } - return result; - } - - /** - * Utility function to sort errors represented as |null| from successes. - * - * @param {string=} operation The name of the operation. If unspecified, - * the name of the caller function. - * @param {pointer} result The result of the operation that may - * represent either an error or a success. If |null|, this function raises - * an error holding ctypes.errno, otherwise it returns |result|. - * @param {string=} path The path of the file. - */ - function throw_on_null(operation, result, path) { - if (result == null || (result.isNull && result.isNull())) { - throw new File.Error(operation, ctypes.errno, path); - } - return result; - } - - /** - * Normalize and verify a Date or numeric date value. - * - * @param {string} fn Function name of the calling function. - * @param {Date,number} date The date to normalize. If omitted or null, - * then the current date will be used. - * - * @throws {TypeError} Invalid date provided. - * - * @return {number} Sanitized, numeric date in milliseconds since epoch. - */ - function normalizeDate(fn, date) { - if (typeof date !== "number" && !date) { - // |date| was Omitted or null. - date = Date.now(); - } else if (typeof date.getTime === "function") { - // Input might be a date or date-like object. - date = date.getTime(); - } - - if (typeof date !== "number" || Number.isNaN(date)) { - throw new TypeError( - "|date| parameter of " + - fn + - " must be a " + - "|Date| instance or number" - ); - } - return date; - } - - /** - * Helper used by both versions of setPermissions. - */ - function unixMode(options) { - let mode = - options.unixMode !== undefined ? options.unixMode : DEFAULT_UNIX_MODE; - let unixHonorUmask = true; - if ("unixHonorUmask" in options) { - unixHonorUmask = options.unixHonorUmask; - } - if (unixHonorUmask) { - mode &= ~SharedAll.Constants.Sys.umask; - } - return mode; - } - - File.Unix = exports.OS.Unix.File; - File.Error = SysAll.Error; - exports.OS.File = File; - exports.OS.Shared.Type = Type; - - Object.defineProperty(File, "POS_START", { value: SysAll.POS_START }); - Object.defineProperty(File, "POS_CURRENT", { value: SysAll.POS_CURRENT }); - Object.defineProperty(File, "POS_END", { value: SysAll.POS_END }); - })(this); -}
deleted file mode 100644 --- a/toolkit/components/osfile/modules/osfile_win_allthreads.jsm +++ /dev/null @@ -1,444 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * This module defines the thread-agnostic components of the Win version - * of OS.File. It depends on the thread-agnostic cross-platform components - * of OS.File. - * - * It serves the following purposes: - * - open kernel32; - * - define OS.Shared.Win.Error; - * - define a few constants and types that need to be defined on all platforms. - * - * This module can be: - * - opened from the main thread as a jsm module; - * - opened from a chrome worker through require(). - */ - -/* eslint-env node */ - -"use strict"; - -var SharedAll; -if (typeof Components != "undefined") { - // Module is opened as a jsm module - const { ctypes } = ChromeUtils.importESModule( - "resource://gre/modules/ctypes.sys.mjs" - ); - // eslint-disable-next-line mozilla/reject-global-this - this.ctypes = ctypes; - - SharedAll = ChromeUtils.import( - "resource://gre/modules/osfile/osfile_shared_allthreads.jsm" - ); - // eslint-disable-next-line mozilla/reject-global-this - this.exports = {}; -} else if (typeof module != "undefined" && typeof require != "undefined") { - // Module is loaded with require() - SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm"); -} else { - throw new Error( - "Please open this module with Component.utils.import or with require()" - ); -} - -SharedAll.LOG.bind(SharedAll, "Win", "allthreads"); -var Const = SharedAll.Constants.Win; - -// Open libc -var libc = new SharedAll.Library("libc", "kernel32.dll"); -exports.libc = libc; - -// Define declareFFI -var declareFFI = SharedAll.declareFFI.bind(null, libc); -exports.declareFFI = declareFFI; - -var Scope = {}; - -// Define Error -libc.declareLazy( - Scope, - "FormatMessage", - "FormatMessageW", - ctypes.winapi_abi, - /* return*/ ctypes.uint32_t, - ctypes.uint32_t, - /* source*/ ctypes.voidptr_t, - ctypes.uint32_t, - /* langid*/ ctypes.uint32_t, - ctypes.char16_t.ptr, - ctypes.uint32_t, - /* Arguments*/ ctypes.voidptr_t -); - -/** - * A File-related error. - * - * To obtain a human-readable error message, use method |toString|. - * To determine the cause of the error, use the various |becauseX| - * getters. To determine the operation that failed, use field - * |operation|. - * - * Additionally, this implementation offers a field - * |winLastError|, which holds the OS-specific error - * constant. If you need this level of detail, you may match the value - * of this field against the error constants of |OS.Constants.Win|. - * - * @param {string=} operation The operation that failed. If unspecified, - * the name of the calling function is taken to be the operation that - * failed. - * @param {number=} lastError The OS-specific constant detailing the - * reason of the error. If unspecified, this is fetched from the system - * status. - * @param {string=} path The file path that manipulated. If unspecified, - * assign the empty string. - * - * @constructor - * @extends {OS.Shared.Error} - */ -var OSError = function OSError( - operation = "unknown operation", - lastError = ctypes.winLastError, - path = "" -) { - SharedAll.OSError.call(this, operation, path); - this.winLastError = lastError; -}; -OSError.prototype = Object.create(SharedAll.OSError.prototype); -OSError.prototype.toString = function toString() { - let buf = new (ctypes.ArrayType(ctypes.char16_t, 1024))(); - let result = Scope.FormatMessage( - Const.FORMAT_MESSAGE_FROM_SYSTEM | Const.FORMAT_MESSAGE_IGNORE_INSERTS, - null, - /* The error number */ this.winLastError, - /* Default language */ 0, - buf, - /* Minimum size of buffer */ 1024, - null - ); - if (!result) { - buf = - "additional error " + - ctypes.winLastError + - " while fetching system error message"; - } - return ( - "Win error " + - this.winLastError + - " during operation " + - this.operation + - (this.path ? " on file " + this.path : "") + - " (" + - buf.readString() + - ")" - ); -}; -OSError.prototype.toMsg = function toMsg() { - return OSError.toMsg(this); -}; - -/** - * |true| if the error was raised because a file or directory - * already exists, |false| otherwise. - */ -Object.defineProperty(OSError.prototype, "becauseExists", { - get: function becauseExists() { - return ( - this.winLastError == Const.ERROR_FILE_EXISTS || - this.winLastError == Const.ERROR_ALREADY_EXISTS - ); - }, -}); -/** - * |true| if the error was raised because a file or directory - * does not exist, |false| otherwise. - */ -Object.defineProperty(OSError.prototype, "becauseNoSuchFile", { - get: function becauseNoSuchFile() { - return ( - this.winLastError == Const.ERROR_FILE_NOT_FOUND || - this.winLastError == Const.ERROR_PATH_NOT_FOUND - ); - }, -}); -/** - * |true| if the error was raised because a directory is not empty - * does not exist, |false| otherwise. - */ -Object.defineProperty(OSError.prototype, "becauseNotEmpty", { - get: function becauseNotEmpty() { - return this.winLastError == Const.ERROR_DIR_NOT_EMPTY; - }, -}); -/** - * |true| if the error was raised because a file or directory - * is closed, |false| otherwise. - */ -Object.defineProperty(OSError.prototype, "becauseClosed", { - get: function becauseClosed() { - return this.winLastError == Const.ERROR_INVALID_HANDLE; - }, -}); -/** - * |true| if the error was raised because permission is denied to - * access a file or directory, |false| otherwise. - */ -Object.defineProperty(OSError.prototype, "becauseAccessDenied", { - get: function becauseAccessDenied() { - return this.winLastError == Const.ERROR_ACCESS_DENIED; - }, -}); -/** - * |true| if the error was raised because some invalid argument was passed, - * |false| otherwise. - */ -Object.defineProperty(OSError.prototype, "becauseInvalidArgument", { - get: function becauseInvalidArgument() { - return ( - this.winLastError == Const.ERROR_NOT_SUPPORTED || - this.winLastError == Const.ERROR_BAD_ARGUMENTS - ); - }, -}); - -/** - * Serialize an instance of OSError to something that can be - * transmitted across threads (not necessarily a string). - */ -OSError.toMsg = function toMsg(error) { - return { - exn: "OS.File.Error", - fileName: error.moduleName, - lineNumber: error.lineNumber, - stack: error.moduleStack, - operation: error.operation, - winLastError: error.winLastError, - path: error.path, - }; -}; - -/** - * Deserialize a message back to an instance of OSError - */ -OSError.fromMsg = function fromMsg(msg) { - let error = new OSError(msg.operation, msg.winLastError, msg.path); - error.stack = msg.stack; - error.fileName = msg.fileName; - error.lineNumber = msg.lineNumber; - return error; -}; -exports.Error = OSError; - -/** - * Code shared by implementation of File.Info on Windows - * - * @constructor - */ -var AbstractInfo = function AbstractInfo( - path, - isDir, - isSymLink, - size, - lastAccessDate, - lastWriteDate, - winAttributes -) { - this._path = path; - this._isDir = isDir; - this._isSymLink = isSymLink; - this._size = size; - this._lastAccessDate = lastAccessDate; - this._lastModificationDate = lastWriteDate; - this._winAttributes = winAttributes; -}; - -AbstractInfo.prototype = { - /** - * The path of the file, used for error-reporting. - * - * @type {string} - */ - get path() { - return this._path; - }, - /** - * |true| if this file is a directory, |false| otherwise - */ - get isDir() { - return this._isDir; - }, - /** - * |true| if this file is a symbolic link, |false| otherwise - */ - get isSymLink() { - return this._isSymLink; - }, - /** - * The size of the file, in bytes. - * - * Note that the result may be |NaN| if the size of the file cannot be - * represented in JavaScript. - * - * @type {number} - */ - get size() { - return this._size; - }, - /** - * The date of last access to this file. - * - * Note that the definition of last access may depend on the underlying - * operating system and file system. - * - * @type {Date} - */ - get lastAccessDate() { - return this._lastAccessDate; - }, - /** - * The date of last modification of this file. - * - * Note that the definition of last access may depend on the underlying - * operating system and file system. - * - * @type {Date} - */ - get lastModificationDate() { - return this._lastModificationDate; - }, - /** - * The Object with following boolean properties of this file. - * {readOnly, system, hidden} - * - * @type {object} - */ - get winAttributes() { - return this._winAttributes; - }, -}; -exports.AbstractInfo = AbstractInfo; - -/** - * Code shared by implementation of File.DirectoryIterator.Entry on Windows - * - * @constructor - */ -var AbstractEntry = function AbstractEntry( - isDir, - isSymLink, - name, - winLastWriteDate, - winLastAccessDate, - path -) { - this._isDir = isDir; - this._isSymLink = isSymLink; - this._name = name; - this._winLastWriteDate = winLastWriteDate; - this._winLastAccessDate = winLastAccessDate; - this._path = path; -}; - -AbstractEntry.prototype = { - /** - * |true| if the entry is a directory, |false| otherwise - */ - get isDir() { - return this._isDir; - }, - /** - * |true| if the entry is a symbolic link, |false| otherwise - */ - get isSymLink() { - return this._isSymLink; - }, - /** - * The name of the entry. - * @type {string} - */ - get name() { - return this._name; - }, - /** - * The last modification time of this file. - * @type {Date} - */ - get winLastWriteDate() { - return this._winLastWriteDate; - }, - /** - * The last access time of this file. - * @type {Date} - */ - get winLastAccessDate() { - return this._winLastAccessDate; - }, - /** - * The full path of the entry - * @type {string} - */ - get path() { - return this._path; - }, -}; -exports.AbstractEntry = AbstractEntry; - -// Special constants that need to be defined on all platforms - -exports.POS_START = Const.FILE_BEGIN; -exports.POS_CURRENT = Const.FILE_CURRENT; -exports.POS_END = Const.FILE_END; - -// Special types that need to be defined for communication -// between threads -var Type = Object.create(SharedAll.Type); -exports.Type = Type; - -/** - * Native paths - * - * Under Windows, expressed as wide strings - */ -Type.path = Type.wstring.withName("[in] path"); -Type.out_path = Type.out_wstring.withName("[out] path"); - -// Special constructors that need to be defined on all threads -OSError.closed = function closed(operation, path) { - return new OSError(operation, Const.ERROR_INVALID_HANDLE, path); -}; - -OSError.exists = function exists(operation, path) { - return new OSError(operation, Const.ERROR_FILE_EXISTS, path); -}; - -OSError.noSuchFile = function noSuchFile(operation, path) { - return new OSError(operation, Const.ERROR_FILE_NOT_FOUND, path); -}; - -OSError.invalidArgument = function invalidArgument(operation) { - return new OSError(operation, Const.ERROR_NOT_SUPPORTED); -}; - -var EXPORTED_SYMBOLS = [ - "declareFFI", - "libc", - "Error", - "AbstractInfo", - "AbstractEntry", - "Type", - "POS_START", - "POS_CURRENT", - "POS_END", -]; - -// ////////// Boilerplate -if (typeof Components != "undefined") { - // eslint-disable-next-line mozilla/reject-global-this - this.EXPORTED_SYMBOLS = EXPORTED_SYMBOLS; - for (let symbol of EXPORTED_SYMBOLS) { - // eslint-disable-next-line mozilla/reject-global-this - this[symbol] = exports[symbol]; - } -}
deleted file mode 100644 --- a/toolkit/components/osfile/modules/osfile_win_back.js +++ /dev/null @@ -1,542 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * This file can be used in the following contexts: - * - * 1. included from a non-osfile worker thread using importScript - * (it serves to define a synchronous API for that worker thread) - * (bug 707681) - * - * 2. included from the main thread using Components.utils.import - * (it serves to define the asynchronous API, whose implementation - * resides in the worker thread) - * (bug 729057) - * - * 3. included from the osfile worker thread using importScript - * (it serves to define the implementation of the asynchronous API) - * (bug 729057) - */ - -/* eslint-env mozilla/chrome-worker, node */ - -// eslint-disable-next-line no-lone-blocks -{ - if (typeof Components != "undefined") { - // We do not wish osfile_win_back.js to be used directly as a main thread - // module yet. When time comes, it will be loaded by a combination of - // a main thread front-end/worker thread implementation that makes sure - // that we are not executing synchronous IO code in the main thread. - - throw new Error( - "osfile_win_back.js cannot be used from the main thread yet" - ); - } - - (function(exports) { - "use strict"; - if (exports.OS && exports.OS.Win && exports.OS.Win.File) { - return; // Avoid double initialization - } - - let SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm"); - let SysAll = require("resource://gre/modules/osfile/osfile_win_allthreads.jsm"); - SharedAll.LOG.bind(SharedAll, "Unix", "back"); - let libc = SysAll.libc; - let advapi32 = new SharedAll.Library("advapi32", "advapi32.dll"); - let Const = SharedAll.Constants.Win; - - /** - * Initialize the Windows module. - * - * @param {function=} declareFFI - */ - // FIXME: Both |init| and |aDeclareFFI| are deprecated, we should remove them - let init = function init(aDeclareFFI) { - let declareFFI; - if (aDeclareFFI) { - declareFFI = aDeclareFFI.bind(null, libc); - } else { - declareFFI = SysAll.declareFFI; // eslint-disable-line no-unused-vars - } - let declareLazyFFI = SharedAll.declareLazyFFI; // eslint-disable-line no-unused-vars - - // Initialize types that require additional OS-specific - // support - either finalization or matching against - // OS-specific constants. - let Type = Object.create(SysAll.Type); - let SysFile = (exports.OS.Win.File = { Type }); - - // Initialize types - - /** - * A C integer holding INVALID_HANDLE_VALUE in case of error or - * a file descriptor in case of success. - */ - Type.HANDLE = Type.voidptr_t.withName("HANDLE"); - Type.HANDLE.importFromC = function importFromC(maybe) { - if (Type.int.cast(maybe).value == INVALID_HANDLE) { - // Ensure that API clients can effectively compare against - // Const.INVALID_HANDLE_VALUE. Without this cast, - // == would always return |false|. - return INVALID_HANDLE; - } - return ctypes.CDataFinalizer(maybe, this.finalizeHANDLE); - }; - Type.HANDLE.finalizeHANDLE = function placeholder() { - throw new Error("finalizeHANDLE should be implemented"); - }; - let INVALID_HANDLE = Const.INVALID_HANDLE_VALUE; - - Type.file_HANDLE = Type.HANDLE.withName("file HANDLE"); - SharedAll.defineLazyGetter( - Type.file_HANDLE, - "finalizeHANDLE", - function() { - return SysFile._CloseHandle; - } - ); - - Type.find_HANDLE = Type.HANDLE.withName("find HANDLE"); - SharedAll.defineLazyGetter( - Type.find_HANDLE, - "finalizeHANDLE", - function() { - return SysFile._FindClose; - } - ); - - Type.DWORD = Type.uint32_t.withName("DWORD"); - - /* A special type used to represent flags passed as DWORDs to a function. - * In JavaScript, bitwise manipulation of numbers, such as or-ing flags, - * can produce negative numbers. Since DWORD is unsigned, these negative - * numbers simply cannot be converted to DWORD. For this reason, whenever - * bit manipulation is called for, you should rather use DWORD_FLAGS, - * which is represented as a signed integer, hence has the correct - * semantics. - */ - Type.DWORD_FLAGS = Type.int32_t.withName("DWORD_FLAGS"); - - /** - * A C integer holding 0 in case of error or a positive integer - * in case of success. - */ - Type.zero_or_DWORD = Type.DWORD.withName("zero_or_DWORD"); - - /** - * A C integer holding 0 in case of error, any other value in - * case of success. - */ - Type.zero_or_nothing = Type.int.withName("zero_or_nothing"); - - /** - * A C integer holding flags related to NTFS security. - */ - Type.SECURITY_ATTRIBUTES = Type.void_t.withName("SECURITY_ATTRIBUTES"); - - /** - * A C integer holding pointers related to NTFS security. - */ - Type.PSID = Type.voidptr_t.withName("PSID"); - - Type.PACL = Type.voidptr_t.withName("PACL"); - - Type.PSECURITY_DESCRIPTOR = Type.voidptr_t.withName( - "PSECURITY_DESCRIPTOR" - ); - - /** - * A C integer holding Win32 local memory handle. - */ - Type.HLOCAL = Type.voidptr_t.withName("HLOCAL"); - - Type.FILETIME = new SharedAll.Type( - "FILETIME", - ctypes.StructType("FILETIME", [ - { lo: Type.DWORD.implementation }, - { hi: Type.DWORD.implementation }, - ]) - ); - - Type.FindData = new SharedAll.Type( - "FIND_DATA", - ctypes.StructType("FIND_DATA", [ - { dwFileAttributes: ctypes.uint32_t }, - { ftCreationTime: Type.FILETIME.implementation }, - { ftLastAccessTime: Type.FILETIME.implementation }, - { ftLastWriteTime: Type.FILETIME.implementation }, - { nFileSizeHigh: Type.DWORD.implementation }, - { nFileSizeLow: Type.DWORD.implementation }, - { dwReserved0: Type.DWORD.implementation }, - { dwReserved1: Type.DWORD.implementation }, - { cFileName: ctypes.ArrayType(ctypes.char16_t, Const.MAX_PATH) }, - { cAlternateFileName: ctypes.ArrayType(ctypes.char16_t, 14) }, - ]) - ); - - Type.FILE_INFORMATION = new SharedAll.Type( - "FILE_INFORMATION", - ctypes.StructType("FILE_INFORMATION", [ - { dwFileAttributes: ctypes.uint32_t }, - { ftCreationTime: Type.FILETIME.implementation }, - { ftLastAccessTime: Type.FILETIME.implementation }, - { ftLastWriteTime: Type.FILETIME.implementation }, - { dwVolumeSerialNumber: ctypes.uint32_t }, - { nFileSizeHigh: Type.DWORD.implementation }, - { nFileSizeLow: Type.DWORD.implementation }, - { nNumberOfLinks: ctypes.uint32_t }, - { nFileIndex: ctypes.uint64_t }, - ]) - ); - - Type.SystemTime = new SharedAll.Type( - "SystemTime", - ctypes.StructType("SystemTime", [ - { wYear: ctypes.int16_t }, - { wMonth: ctypes.int16_t }, - { wDayOfWeek: ctypes.int16_t }, - { wDay: ctypes.int16_t }, - { wHour: ctypes.int16_t }, - { wMinute: ctypes.int16_t }, - { wSecond: ctypes.int16_t }, - { wMilliSeconds: ctypes.int16_t }, - ]) - ); - - // Special case: these functions are used by the - // finalizer - libc.declareLazy( - SysFile, - "_CloseHandle", - "CloseHandle", - ctypes.winapi_abi, - /* return */ ctypes.bool, - /* handle*/ ctypes.voidptr_t - ); - - SysFile.CloseHandle = function(fd) { - if (fd == INVALID_HANDLE) { - return true; - } - return fd.dispose(); // Returns the value of |CloseHandle|. - }; - - libc.declareLazy( - SysFile, - "_FindClose", - "FindClose", - ctypes.winapi_abi, - /* return */ ctypes.bool, - /* handle*/ ctypes.voidptr_t - ); - - SysFile.FindClose = function(handle) { - if (handle == INVALID_HANDLE) { - return true; - } - return handle.dispose(); // Returns the value of |FindClose|. - }; - - // Declare libc functions as functions of |OS.Win.File| - - libc.declareLazyFFI( - SysFile, - "CopyFile", - "CopyFileW", - ctypes.winapi_abi, - /* return*/ Type.zero_or_nothing, - /* sourcePath*/ Type.path, - Type.path, - /* bailIfExist*/ Type.bool - ); - - libc.declareLazyFFI( - SysFile, - "CreateDirectory", - "CreateDirectoryW", - ctypes.winapi_abi, - /* return*/ Type.zero_or_nothing, - Type.char16_t.in_ptr, - /* security*/ Type.SECURITY_ATTRIBUTES.in_ptr - ); - - libc.declareLazyFFI( - SysFile, - "CreateFile", - "CreateFileW", - ctypes.winapi_abi, - Type.file_HANDLE, - Type.path, - Type.DWORD_FLAGS, - Type.DWORD_FLAGS, - /* security*/ Type.SECURITY_ATTRIBUTES.in_ptr, - /* creation*/ Type.DWORD_FLAGS, - Type.DWORD_FLAGS, - /* template*/ Type.HANDLE - ); - - libc.declareLazyFFI( - SysFile, - "DeleteFile", - "DeleteFileW", - ctypes.winapi_abi, - /* return*/ Type.zero_or_nothing, - Type.path - ); - - libc.declareLazyFFI( - SysFile, - "FileTimeToSystemTime", - "FileTimeToSystemTime", - ctypes.winapi_abi, - /* return*/ Type.zero_or_nothing, - /* filetime*/ Type.FILETIME.in_ptr, - /* systime*/ Type.SystemTime.out_ptr - ); - - libc.declareLazyFFI( - SysFile, - "SystemTimeToFileTime", - "SystemTimeToFileTime", - ctypes.winapi_abi, - Type.zero_or_nothing, - Type.SystemTime.in_ptr, - /* filetime*/ Type.FILETIME.out_ptr - ); - - libc.declareLazyFFI( - SysFile, - "FindFirstFile", - "FindFirstFileW", - ctypes.winapi_abi, - /* return*/ Type.find_HANDLE, - /* pattern*/ Type.path, - Type.FindData.out_ptr - ); - - libc.declareLazyFFI( - SysFile, - "FindNextFile", - "FindNextFileW", - ctypes.winapi_abi, - /* return*/ Type.zero_or_nothing, - Type.find_HANDLE, - Type.FindData.out_ptr - ); - - libc.declareLazyFFI( - SysFile, - "FormatMessage", - "FormatMessageW", - ctypes.winapi_abi, - /* return*/ Type.DWORD, - Type.DWORD_FLAGS, - /* source*/ Type.void_t.in_ptr, - Type.DWORD_FLAGS, - /* langid*/ Type.DWORD_FLAGS, - Type.out_wstring, - Type.DWORD, - /* Arguments*/ Type.void_t.in_ptr - ); - - libc.declareLazyFFI( - SysFile, - "GetCurrentDirectory", - "GetCurrentDirectoryW", - ctypes.winapi_abi, - /* return*/ Type.zero_or_DWORD, - /* length*/ Type.DWORD, - Type.out_path - ); - - libc.declareLazyFFI( - SysFile, - "GetFullPathName", - "GetFullPathNameW", - ctypes.winapi_abi, - Type.zero_or_DWORD, - /* fileName*/ Type.path, - Type.DWORD, - Type.out_path, - /* filePart*/ Type.DWORD - ); - - libc.declareLazyFFI( - SysFile, - "GetDiskFreeSpaceEx", - "GetDiskFreeSpaceExW", - ctypes.winapi_abi, - /* return*/ Type.zero_or_nothing, - /* directoryName*/ Type.path, - /* freeBytesForUser*/ Type.uint64_t.out_ptr, - /* totalBytesForUser*/ Type.uint64_t.out_ptr, - /* freeTotalBytesOnDrive*/ Type.uint64_t.out_ptr - ); - - libc.declareLazyFFI( - SysFile, - "GetFileInformationByHandle", - "GetFileInformationByHandle", - ctypes.winapi_abi, - /* return*/ Type.zero_or_nothing, - /* handle*/ Type.HANDLE, - Type.FILE_INFORMATION.out_ptr - ); - - libc.declareLazyFFI( - SysFile, - "MoveFileEx", - "MoveFileExW", - ctypes.winapi_abi, - Type.zero_or_nothing, - /* sourcePath*/ Type.path, - /* destPath*/ Type.path, - Type.DWORD - ); - - libc.declareLazyFFI( - SysFile, - "ReadFile", - "ReadFile", - ctypes.winapi_abi, - /* return*/ Type.zero_or_nothing, - Type.HANDLE, - /* buffer*/ Type.voidptr_t, - /* nbytes*/ Type.DWORD, - /* nbytes_read*/ Type.DWORD.out_ptr, - /* overlapped*/ Type.void_t.inout_ptr // FIXME: Implement? - ); - - libc.declareLazyFFI( - SysFile, - "RemoveDirectory", - "RemoveDirectoryW", - ctypes.winapi_abi, - /* return*/ Type.zero_or_nothing, - Type.path - ); - - libc.declareLazyFFI( - SysFile, - "SetEndOfFile", - "SetEndOfFile", - ctypes.winapi_abi, - /* return*/ Type.zero_or_nothing, - Type.HANDLE - ); - - libc.declareLazyFFI( - SysFile, - "SetFilePointer", - "SetFilePointer", - ctypes.winapi_abi, - /* return*/ Type.DWORD, - Type.HANDLE, - /* distlow*/ Type.long, - /* disthi*/ Type.long.in_ptr, - /* method*/ Type.DWORD - ); - - libc.declareLazyFFI( - SysFile, - "SetFileTime", - "SetFileTime", - ctypes.winapi_abi, - Type.zero_or_nothing, - Type.HANDLE, - /* creation*/ Type.FILETIME.in_ptr, - Type.FILETIME.in_ptr, - Type.FILETIME.in_ptr - ); - - libc.declareLazyFFI( - SysFile, - "WriteFile", - "WriteFile", - ctypes.winapi_abi, - /* return*/ Type.zero_or_nothing, - Type.HANDLE, - /* buffer*/ Type.voidptr_t, - /* nbytes*/ Type.DWORD, - /* nbytes_wr*/ Type.DWORD.out_ptr, - /* overlapped*/ Type.void_t.inout_ptr // FIXME: Implement? - ); - - libc.declareLazyFFI( - SysFile, - "FlushFileBuffers", - "FlushFileBuffers", - ctypes.winapi_abi, - /* return*/ Type.zero_or_nothing, - Type.HANDLE - ); - - libc.declareLazyFFI( - SysFile, - "GetFileAttributes", - "GetFileAttributesW", - ctypes.winapi_abi, - Type.DWORD_FLAGS, - /* fileName*/ Type.path - ); - - libc.declareLazyFFI( - SysFile, - "SetFileAttributes", - "SetFileAttributesW", - ctypes.winapi_abi, - Type.zero_or_nothing, - Type.path, - /* fileAttributes*/ Type.DWORD_FLAGS - ); - - advapi32.declareLazyFFI( - SysFile, - "GetNamedSecurityInfo", - "GetNamedSecurityInfoW", - ctypes.winapi_abi, - Type.DWORD, - Type.path, - Type.DWORD, - /* securityInfo*/ Type.DWORD, - Type.PSID.out_ptr, - Type.PSID.out_ptr, - Type.PACL.out_ptr, - Type.PACL.out_ptr, - /* securityDesc*/ Type.PSECURITY_DESCRIPTOR.out_ptr - ); - - advapi32.declareLazyFFI( - SysFile, - "SetNamedSecurityInfo", - "SetNamedSecurityInfoW", - ctypes.winapi_abi, - Type.DWORD, - Type.path, - Type.DWORD, - /* securityInfo*/ Type.DWORD, - Type.PSID, - Type.PSID, - Type.PACL, - Type.PACL - ); - - libc.declareLazyFFI( - SysFile, - "LocalFree", - "LocalFree", - ctypes.winapi_abi, - Type.HLOCAL, - Type.HLOCAL - ); - }; - - exports.OS.Win = { - File: { - _init: init, - }, - }; - })(this); -}
deleted file mode 100644 --- a/toolkit/components/osfile/modules/osfile_win_front.js +++ /dev/null @@ -1,1322 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Synchronous front-end for the JavaScript OS.File library. - * Windows implementation. - * - * This front-end is meant to be imported by a worker thread. - */ - -/* eslint-env mozilla/chrome-worker, node */ -/* global OS */ - -// eslint-disable-next-line no-lone-blocks -{ - if (typeof Components != "undefined") { - // We do not wish osfile_win_front.js to be used directly as a main thread - // module yet. - throw new Error( - "osfile_win_front.js cannot be used from the main thread yet" - ); - } - - (function(exports) { - "use strict"; - - // exports.OS.Win is created by osfile_win_back.js - if (exports.OS && exports.OS.File) { - return; // Avoid double-initialization - } - - let SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm"); - let Path = require("resource://gre/modules/osfile/ospath.jsm"); - let SysAll = require("resource://gre/modules/osfile/osfile_win_allthreads.jsm"); - exports.OS.Win.File._init(); - let Const = exports.OS.Constants.Win; - let WinFile = exports.OS.Win.File; - let Type = WinFile.Type; - - // Mutable thread-global data - // In the Windows implementation, methods |read| and |write| - // require passing a pointer to an uint32 to determine how many - // bytes have been read/written. In C, this is a benigne operation, - // but in js-ctypes, this has a cost. Rather than re-allocating a - // C uint32 and a C uint32* for each |read|/|write|, we take advantage - // of the fact that the state is thread-private -- hence that two - // |read|/|write| operations cannot take place at the same time -- - // and we use the following global mutable values: - let gBytesRead = new ctypes.uint32_t(0); - let gBytesReadPtr = gBytesRead.address(); - let gBytesWritten = new ctypes.uint32_t(0); - let gBytesWrittenPtr = gBytesWritten.address(); - - // Same story for GetFileInformationByHandle - let gFileInfo = new Type.FILE_INFORMATION.implementation(); - let gFileInfoPtr = gFileInfo.address(); - - /** - * Representation of a file. - * - * You generally do not need to call this constructor yourself. Rather, - * to open a file, use function |OS.File.open|. - * - * @param fd A OS-specific file descriptor. - * @param {string} path File path of the file handle, used for error-reporting. - * @constructor - */ - let File = function File(fd, path) { - exports.OS.Shared.AbstractFile.call(this, fd, path); - this._closeResult = null; - }; - File.prototype = Object.create(exports.OS.Shared.AbstractFile.prototype); - - /** - * Close the file. - * - * This method has no effect if the file is already closed. However, - * if the first call to |close| has thrown an error, further calls - * will throw the same error. - * - * @throws File.Error If closing the file revealed an error that could - * not be reported earlier. - */ - File.prototype.close = function close() { - if (this._fd) { - let fd = this._fd; - this._fd = null; - // Call |close(fd)|, detach finalizer if any - // (|fd| may not be a CDataFinalizer if it has been - // instantiated from a controller thread). - let result = WinFile._CloseHandle(fd); - if (typeof fd == "object" && "forget" in fd) { - fd.forget(); - } - if (result == -1) { - this._closeResult = new File.Error( - "close", - ctypes.winLastError, - this._path - ); - } - } - if (this._closeResult) { - throw this._closeResult; - } - }; - - /** - * Read some bytes from a file. - * - * @param {C pointer} buffer A buffer for holding the data - * once it is read. - * @param {number} nbytes The number of bytes to read. It must not - * exceed the size of |buffer| in bytes but it may exceed the number - * of bytes unread in the file. - * @param {*=} options Additional options for reading. Ignored in - * this implementation. - * - * @return {number} The number of bytes effectively read. If zero, - * the end of the file has been reached. - * @throws {OS.File.Error} In case of I/O error. - */ - File.prototype._read = function _read(buffer, nbytes, options) { - // |gBytesReadPtr| is a pointer to |gBytesRead|. - throw_on_zero( - "read", - WinFile.ReadFile(this.fd, buffer, nbytes, gBytesReadPtr, null), - this._path - ); - return gBytesRead.value; - }; - - /** - * Write some bytes to a file. - * - * @param {Typed array} buffer A buffer holding the data that must be - * written. - * @param {number} nbytes The number of bytes to write. It must not - * exceed the size of |buffer| in bytes. - * @param {*=} options Additional options for writing. Ignored in - * this implementation. - * - * @return {number} The number of bytes effectively written. - * @throws {OS.File.Error} In case of I/O error. - */ - File.prototype._write = function _write(buffer, nbytes, options) { - if (this._appendMode) { - // Need to manually seek on Windows, as O_APPEND is not supported. - // This is, of course, a race, but there is no real way around this. - this.setPosition(0, File.POS_END); - } - // |gBytesWrittenPtr| is a pointer to |gBytesWritten|. - throw_on_zero( - "write", - WinFile.WriteFile(this.fd, buffer, nbytes, gBytesWrittenPtr, null), - this._path - ); - return gBytesWritten.value; - }; - - /** - * Return the current position in the file. - */ - File.prototype.getPosition = function getPosition(pos) { - return this.setPosition(0, File.POS_CURRENT); - }; - - /** - * Change the current position in the file. - * - * @param {number} pos The new position. Whether this position - * is considered from the current position, from the start of - * the file or from the end of the file is determined by - * argument |whence|. Note that |pos| may exceed the length of - * the file. - * @param {number=} whence The reference position. If omitted - * or |OS.File.POS_START|, |pos| is relative to the start of the - * file. If |OS.File.POS_CURRENT|, |pos| is relative to the - * current position in the file. If |OS.File.POS_END|, |pos| is - * relative to the end of the file. - * - * @return The new position in the file. - */ - File.prototype.setPosition = function setPosition(pos, whence) { - if (whence === undefined) { - whence = Const.FILE_BEGIN; - } - let pos64 = ctypes.Int64(pos); - // Per MSDN, while |lDistanceToMove| (low) is declared as int32_t, when - // providing |lDistanceToMoveHigh| as well, it should countain the - // bottom 32 bits of the 64-bit integer. Hence the following |posLo| - // cast is OK. - let posLo = new ctypes.uint32_t(ctypes.Int64.lo(pos64)); - posLo = ctypes.cast(posLo, ctypes.int32_t); - let posHi = new ctypes.int32_t(ctypes.Int64.hi(pos64)); - let result = WinFile.SetFilePointer( - this.fd, - posLo.value, - posHi.address(), - whence - ); - // INVALID_SET_FILE_POINTER might be still a valid result, as it - // represents the lower 32 bit of the int64 result. MSDN says to check - // both, INVALID_SET_FILE_POINTER and a non-zero winLastError. - if (result == Const.INVALID_SET_FILE_POINTER && ctypes.winLastError) { - throw new File.Error("setPosition", ctypes.winLastError, this._path); - } - pos64 = ctypes.Int64.join(posHi.value, result); - return Type.int64_t.project(pos64); - }; - - /** - * Fetch the information on the file. - * - * @return File.Info The information on |this| file. - */ - File.prototype.stat = function stat() { - throw_on_zero( - "stat", - WinFile.GetFileInformationByHandle(this.fd, gFileInfoPtr), - this._path - ); - return new File.Info(gFileInfo, this._path); - }; - - /** - * Set the last access and modification date of the file. - * The time stamp resolution is 1 second at best, but might be worse - * depending on the platform. - * - * @param {Date,number=} accessDate The last access date. If numeric, - * milliseconds since epoch. If omitted or null, then the current date - * will be used. - * @param {Date,number=} modificationDate The last modification date. If - * numeric, milliseconds since epoch. If omitted or null, then the current - * date will be used. - * - * @throws {TypeError} In case of invalid parameters. - * @throws {OS.File.Error} In case of I/O error. - */ - File.prototype.setDates = function setDates(accessDate, modificationDate) { - accessDate = Date_to_FILETIME( - "File.prototype.setDates", - accessDate, - this._path - ); - modificationDate = Date_to_FILETIME( - "File.prototype.setDates", - modificationDate, - this._path - ); - throw_on_zero( - "setDates", - WinFile.SetFileTime( - this.fd, - null, - accessDate.address(), - modificationDate.address() - ), - this._path - ); - }; - - /** - * Set the file's access permission bits. - */ - File.prototype.setPermissions = function setPermissions(options = {}) { - if (!("winAttributes" in options)) { - return; - } - let oldAttributes = WinFile.GetFileAttributes(this._path); - if (oldAttributes == Const.INVALID_FILE_ATTRIBUTES) { - throw new File.Error("setPermissions", ctypes.winLastError, this._path); - } - let newAttributes = toFileAttributes( - options.winAttributes, - oldAttributes - ); - throw_on_zero( - "setPermissions", - WinFile.SetFileAttributes(this._path, newAttributes), - this._path - ); - }; - - /** - * Flushes the file's buffers and causes all buffered data - * to be written. - * Disk flushes are very expensive and therefore should be used carefully, - * sparingly and only in scenarios where it is vital that data survives - * system crashes. Even though the function will be executed off the - * main-thread, it might still affect the overall performance of any - * running application. - * - * @throws {OS.File.Error} In case of I/O error. - */ - File.prototype.flush = function flush() { - throw_on_zero("flush", WinFile.FlushFileBuffers(this.fd), this._path); - }; - - // The default sharing mode for opening files: files are not - // locked against being reopened for reading/writing or against - // being deleted by the same process or another process. - // This is consistent with the default Unix policy. - const DEFAULT_SHARE = - Const.FILE_SHARE_READ | Const.FILE_SHARE_WRITE | Const.FILE_SHARE_DELETE; - - // The default flags for opening files. - const DEFAULT_FLAGS = Const.FILE_ATTRIBUTE_NORMAL; - - /** - * Open a file - * - * @param {string} path The path to the file. - * @param {*=} mode The opening mode for the file, as - * an object that may contain the following fields: - * - * - {bool} truncate If |true|, the file will be opened - * for writing. If the file does not exist, it will be - * created. If the file exists, its contents will be - * erased. Cannot be specified with |create|. - * - {bool} create If |true|, the file will be opened - * for writing. If the file exists, this function fails. - * If the file does not exist, it will be created. Cannot - * be specified with |truncate| or |existing|. - * - {bool} existing. If the file does not exist, this function - * fails. Cannot be specified with |create|. - * - {bool} read If |true|, the file will be opened for - * reading. The file may also be opened for writing, depending - * on the other fields of |mode|. - * - {bool} write If |true|, the file will be opened for - * writing. The file may also be opened for reading, depending - * on the other fields of |mode|. - * - {bool} append If |true|, the file will be opened for appending, - * meaning the equivalent of |.setPosition(0, POS_END)| is executed - * before each write. The default is |true|, i.e. opening a file for - * appending. Specify |append: false| to open the file in regular mode. - * - * If neither |truncate|, |create| or |write| is specified, the file - * is opened for reading. - * - * Note that |false|, |null| or |undefined| flags are simply ignored. - * - * @param {*=} options Additional options for file opening. This - * implementation interprets the following fields: - * - * - {number} winShare If specified, a share mode, as per - * Windows function |CreateFile|. You can build it from - * constants |OS.Constants.Win.FILE_SHARE_*|. If unspecified, - * the file uses the default sharing policy: it can be opened - * for reading and/or writing and it can be removed by other - * processes and by the same process. - * - {number} winSecurity If specified, Windows security - * attributes, as per Windows function |CreateFile|. If unspecified, - * no security attributes. - * - {number} winAccess If specified, Windows access mode, as - * per Windows function |CreateFile|. This also requires option - * |winDisposition| and this replaces argument |mode|. If unspecified, - * uses the string |mode|. - * - {number} winDisposition If specified, Windows disposition mode, - * as per Windows function |CreateFile|. This also requires option - * |winAccess| and this replaces argument |mode|. If unspecified, - * uses the string |mode|. - * - * @return {File} A file object. - * @throws {OS.File.Error} If the file could not be opened. - */ - File.open = function Win_open(path, mode = {}, options = {}) { - let share = - options.winShare !== undefined ? options.winShare : DEFAULT_SHARE; - let security = options.winSecurity || null; - let flags = - options.winFlags !== undefined ? options.winFlags : DEFAULT_FLAGS; - let template = options.winTemplate ? options.winTemplate._fd : null; - let access; - let disposition; - - mode = OS.Shared.AbstractFile.normalizeOpenMode(mode); - - // The following option isn't a generic implementation of access to paths - // of arbitrary lengths. It allows for the specific case of writing to an - // Alternate Data Stream on a file whose path length is already close to - // MAX_PATH. This implementation is safe with a full path as input, if - // the first part of the path comes from local configuration and the - // file without the ADS was successfully opened before, so we know the - // path is valid. - if (options.winAllowLengthBeyondMaxPathWithCaveats) { - // Use the \\?\ syntax to allow lengths beyond MAX_PATH. This limited - // implementation only supports a DOS local path or UNC path as input. - let isUNC = - path.length >= 2 && - (path[0] == "\\" || path[0] == "/") && - (path[1] == "\\" || path[1] == "/"); - let pathToUse = "\\\\?\\" + (isUNC ? "UNC\\" + path.slice(2) : path); - // Use GetFullPathName to normalize slashes into backslashes. This is - // required because CreateFile won't do this for the \\?\ syntax. - let buffer_size = 512; - let array = new (ctypes.ArrayType(ctypes.char16_t, buffer_size))(); - let expected_size = throw_on_zero( - "open", - WinFile.GetFullPathName(pathToUse, buffer_size, array, 0) - ); - if (expected_size > buffer_size) { - // We don't need to allow an arbitrary path length for now. - throw new File.Error("open", ctypes.winLastError, path); - } - path = array.readString(); - } - - if ("winAccess" in options && "winDisposition" in options) { - access = options.winAccess; - disposition = options.winDisposition; - } else if ( - ("winAccess" in options && !("winDisposition" in options)) || - (!("winAccess" in options) && "winDisposition" in options) - ) { - throw new TypeError( - "OS.File.open requires either both options " + - "winAccess and winDisposition or neither" - ); - } else { - if (mode.read) { - access |= Const.GENERIC_READ; - } - if (mode.write) { - access |= Const.GENERIC_WRITE; - } - // Finally, handle create/existing/trunc - if (mode.trunc) { - if (mode.existing) { - // It seems that Const.TRUNCATE_EXISTING is broken - // in presence of links (source, anyone?). We need - // to open normally, then perform truncation manually. - disposition = Const.OPEN_EXISTING; - } else { - disposition = Const.CREATE_ALWAYS; - } - } else if (mode.create) { - disposition = Const.CREATE_NEW; - } else if (mode.read && !mode.write) { - disposition = Const.OPEN_EXISTING; - } else if (mode.existing) { - disposition = Const.OPEN_EXISTING; - } else { - disposition = Const.OPEN_ALWAYS; - } - } - - let file = error_or_file( - WinFile.CreateFile( - path, - access, - share, - security, - disposition, - flags, - template - ), - path - ); - - file._appendMode = !!mode.append; - - if (!(mode.trunc && mode.existing)) { - return file; - } - // Now, perform manual truncation - file.setPosition(0, File.POS_START); - throw_on_zero("open", WinFile.SetEndOfFile(file.fd), path); - return file; - }; - - /** - * Checks if a file or directory exists - * - * @param {string} path The path to the file. - * - * @return {bool} true if the file exists, false otherwise. - */ - File.exists = function Win_exists(path) { - try { - let file = File.open(path, FILE_STAT_MODE, FILE_STAT_OPTIONS); - file.close(); - return true; - } catch (x) { - return false; - } - }; - - /** - * Remove an existing file. - * - * @param {string} path The name of the file. - * @param {*=} options Additional options. - * - {bool} ignoreAbsent If |false|, throw an error if the file does - * not exist. |true| by default. - * - * @throws {OS.File.Error} In case of I/O error. - */ - File.remove = function remove(path, options = {}) { - if (WinFile.DeleteFile(path)) { - return; - } - - if ( - ctypes.winLastError == Const.ERROR_FILE_NOT_FOUND || - ctypes.winLastError == Const.ERROR_PATH_NOT_FOUND - ) { - if (!("ignoreAbsent" in options) || options.ignoreAbsent) { - return; - } - } else if (ctypes.winLastError == Const.ERROR_ACCESS_DENIED) { - // Save winLastError before another ctypes call. - let lastError = ctypes.winLastError; - let attributes = WinFile.GetFileAttributes(path); - if (attributes != Const.INVALID_FILE_ATTRIBUTES) { - if (!(attributes & Const.FILE_ATTRIBUTE_READONLY)) { - throw new File.Error("remove", lastError, path); - } - let newAttributes = attributes & ~Const.FILE_ATTRIBUTE_READONLY; - if ( - WinFile.SetFileAttributes(path, newAttributes) && - WinFile.DeleteFile(path) - ) { - return; - } - } - } - - throw new File.Error("remove", ctypes.winLastError, path); - }; - - /** - * Remove an empty directory. - * - * @param {string} path The name of the directory to remove. - * @param {*=} options Additional options. - * - {bool} ignoreAbsent If |false|, throw an error if the directory - * does not exist. |true| by default - */ - File.removeEmptyDir = function removeEmptyDir(path, options = {}) { - let result = WinFile.RemoveDirectory(path); - if (!result) { - if ( - (!("ignoreAbsent" in options) || options.ignoreAbsent) && - ctypes.winLastError == Const.ERROR_FILE_NOT_FOUND - ) { - return; - } - throw new File.Error("removeEmptyDir", ctypes.winLastError, path); - } - }; - - /** - * Create a directory and, optionally, its parent directories. - * - * @param {string} path The name of the directory. - * @param {*=} options Additional options. This - * implementation interprets the following fields: - * - * - {C pointer} winSecurity If specified, security attributes - * as per winapi function |CreateDirectory|. If unspecified, - * use the default security descriptor, inherited from the - * parent directory. - * - {bool} ignoreExisting If |false|, throw an error if the directory - * already exists. |true| by default - * - {string} from If specified, the call to |makeDir| creates all the - * ancestors of |path| that are descendants of |from|. Note that |from| - * and its existing descendants must be user-writeable and that |path| - * must be a descendant of |from|. - * Example: - * makeDir(Path.join(profileDir, "foo", "bar"), { from: profileDir }); - * creates directories profileDir/foo, profileDir/foo/bar - */ - File._makeDir = function makeDir(path, options = {}) { - let security = options.winSecurity || null; - let result = WinFile.CreateDirectory(path, security); - - if (result) { - return; - } - - if ("ignoreExisting" in options && !options.ignoreExisting) { - throw new File.Error("makeDir", ctypes.winLastError, path); - } - - if (ctypes.winLastError == Const.ERROR_ALREADY_EXISTS) { - return; - } - - // If the user has no access, but it's a root directory, no error should be thrown - let splitPath = OS.Path.split(path); - // Removing last component if it's empty - // An empty last component is caused by trailing slashes in path - // This is always the case with root directories - if (splitPath.components[splitPath.components.length - 1].length === 0) { - splitPath.components.pop(); - } - // One component consisting of a drive letter implies a directory root. - if ( - ctypes.winLastError == Const.ERROR_ACCESS_DENIED && - splitPath.winDrive && - splitPath.components.length === 1 - ) { - return; - } - - throw new File.Error("makeDir", ctypes.winLastError, path); - }; - - /** - * Copy a file to a destination. - * - * @param {string} sourcePath The platform-specific path at which - * the file may currently be found. - * @param {string} destPath The platform-specific path at which the - * file should be copied. - * @param {*=} options An object which may contain the following fields: - * - * @option {bool} noOverwrite - If true, this function will fail if - * a file already exists at |destPath|. Otherwise, if this file exists, - * it will be erased silently. - * - * @throws {OS.File.Error} In case of any error. - * - * General note: The behavior of this function is defined only when - * it is called on a single file. If it is called on a directory, the - * behavior is undefined and may not be the same across all platforms. - * - * General note: The behavior of this function with respect to metadata - * is unspecified. Metadata may or may not be copied with the file. The - * behavior may not be the same across all platforms. - */ - File.copy = function copy(sourcePath, destPath, options = {}) { - throw_on_zero( - "copy", - WinFile.CopyFile(sourcePath, destPath, options.noOverwrite || false), - sourcePath - ); - }; - - /** - * Move a file to a destination. - * - * @param {string} sourcePath The platform-specific path at which - * the file may currently be found. - * @param {string} destPath The platform-specific path at which the - * file should be moved. - * @param {*=} options An object which may contain the following fields: - * - * @option {bool} noOverwrite - If set, this function will fail if - * a file already exists at |destPath|. Otherwise, if this file exists, - * it will be erased silently. - * @option {bool} noCopy - If set, this function will fail if the - * operation is more sophisticated than a simple renaming, i.e. if - * |sourcePath| and |destPath| are not situated on the same drive. - * - * @throws {OS.File.Error} In case of any error. - * - * General note: The behavior of this function is defined only when - * it is called on a single file. If it is called on a directory, the - * behavior is undefined and may not be the same across all platforms. - * - * General note: The behavior of this function with respect to metadata - * is unspecified. Metadata may or may not be moved with the file. The - * behavior may not be the same across all platforms. - */ - File.move = function move(sourcePath, destPath, options = {}) { - let flags = 0; - if (!options.noCopy) { - flags = Const.MOVEFILE_COPY_ALLOWED; - } - if (!options.noOverwrite) { - flags = flags | Const.MOVEFILE_REPLACE_EXISTING; - } - throw_on_zero( - "move", - WinFile.MoveFileEx(sourcePath, destPath, flags), - sourcePath - ); - - // Inherit NTFS permissions from the destination directory - // if possible. - if (Path.dirname(sourcePath) === Path.dirname(destPath)) { - // Skip if the move operation was the simple rename, - return; - } - // The function may fail for various reasons (e.g. not all - // filesystems support NTFS permissions or the user may not - // have the enough rights to read/write permissions). - // However we can safely ignore errors. The file was already - // moved. Setting permissions is not mandatory. - let dacl = new ctypes.voidptr_t(); - let sd = new ctypes.voidptr_t(); - WinFile.GetNamedSecurityInfo( - destPath, - Const.SE_FILE_OBJECT, - Const.DACL_SECURITY_INFORMATION, - null /* sidOwner*/, - null /* sidGroup*/, - dacl.address(), - null /* sacl*/, - sd.address() - ); - // dacl will be set only if the function succeeds. - if (!dacl.isNull()) { - WinFile.SetNamedSecurityInfo( - destPath, - Const.SE_FILE_OBJECT, - Const.DACL_SECURITY_INFORMATION | - Const.UNPROTECTED_DACL_SECURITY_INFORMATION, - null /* sidOwner*/, - null /* sidGroup*/, - dacl, - null /* sacl*/ - ); - } - // sd will be set only if the function succeeds. - if (!sd.isNull()) { - WinFile.LocalFree(Type.HLOCAL.cast(sd)); - } - }; - - /** - * A global value used to receive data during time conversions. - */ - let gSystemTime = new Type.SystemTime.implementation(); - let gSystemTimePtr = gSystemTime.address(); - - /** - * Utility function: convert a FILETIME to a JavaScript Date. - */ - let FILETIME_to_Date = function FILETIME_to_Date(fileTime, path) { - if (fileTime == null) { - throw new TypeError("Expecting a non-null filetime"); - } - throw_on_zero( - "FILETIME_to_Date", - WinFile.FileTimeToSystemTime(fileTime.address(), gSystemTimePtr), - path - ); - // Windows counts hours, minutes, seconds from UTC, - // JS counts from local time, so we need to go through UTC. - let utc = Date.UTC( - gSystemTime.wYear, - gSystemTime.wMonth - 1, - /* Windows counts months from 1, JS from 0*/ gSystemTime.wDay, - gSystemTime.wHour, - gSystemTime.wMinute, - gSystemTime.wSecond, - gSystemTime.wMilliSeconds - ); - return new Date(utc); - }; - - /** - * Utility function: convert Javascript Date to FileTime. - * - * @param {string} fn Name of the calling function. - * @param {Date,number} date The date to be converted. If omitted or null, - * then the current date will be used. If numeric, assumed to be the date - * in milliseconds since epoch. - */ - let Date_to_FILETIME = function Date_to_FILETIME(fn, date, path) { - if (typeof date === "number") { - date = new Date(date); - } else if (!date) { - date = new Date(); - } else if (typeof date.getUTCFullYear !== "function") { - throw new TypeError( - "|date| parameter of " + - fn + - " must be a " + - "|Date| instance or number" - ); - } - gSystemTime.wYear = date.getUTCFullYear(); - // Windows counts months from 1, JS from 0. - gSystemTime.wMonth = date.getUTCMonth() + 1; - gSystemTime.wDay = date.getUTCDate(); - gSystemTime.wHour = date.getUTCHours(); - gSystemTime.wMinute = date.getUTCMinutes(); - gSystemTime.wSecond = date.getUTCSeconds(); - gSystemTime.wMilliseconds = date.getUTCMilliseconds(); - let result = new OS.Shared.Type.FILETIME.implementation(); - throw_on_zero( - "Date_to_FILETIME", - WinFile.SystemTimeToFileTime(gSystemTimePtr, result.address()), - path - ); - return result; - }; - - /** - * Iterate on one directory. - * - * This iterator will not enter subdirectories. - * - * @param {string} path The directory upon which to iterate. - * @param {*=} options An object that may contain the following field: - * @option {string} winPattern Windows file name pattern; if set, - * only files matching this pattern are returned. - * - * @throws {File.Error} If |path| does not represent a directory or - * if the directory cannot be iterated. - * @constructor - */ - File.DirectoryIterator = function DirectoryIterator(path, options) { - exports.OS.Shared.AbstractFile.AbstractIterator.call(this); - if (options && options.winPattern) { - this._pattern = path + "\\" + options.winPattern; - } else { - this._pattern = path + "\\*"; - } - this._path = path; - - // Pre-open the first item. - this._first = true; - this._findData = new Type.FindData.implementation(); - this._findDataPtr = this._findData.address(); - this._handle = WinFile.FindFirstFile(this._pattern, this._findDataPtr); - if (this._handle == Const.INVALID_HANDLE_VALUE) { - let error = ctypes.winLastError; - this._findData = null; - this._findDataPtr = null; - if (error == Const.ERROR_FILE_NOT_FOUND) { - // Directory is empty, let's behave as if it were closed - SharedAll.LOG("Directory is empty"); - this._closed = true; - this._exists = true; - } else if (error == Const.ERROR_PATH_NOT_FOUND) { - // Directory does not exist, let's throw if we attempt to walk it - SharedAll.LOG("Directory does not exist"); - this._closed = true; - this._exists = false; - } else { - throw new File.Error("DirectoryIterator", error, this._path); - } - } else { - this._closed = false; - this._exists = true; - } - }; - - File.DirectoryIterator.prototype = Object.create( - exports.OS.Shared.AbstractFile.AbstractIterator.prototype - ); - - /** - * Fetch the next entry in the directory. - * - * @return null If we have reached the end of the directory. - */ - File.DirectoryIterator.prototype._next = function _next() { - // Bailout if the directory does not exist - if (!this._exists) { - throw File.Error.noSuchFile( - "DirectoryIterator.prototype.next", - this._path - ); - } - // Bailout if the iterator is closed. - if (this._closed) { - return null; - } - // If this is the first entry, we have obtained it already - // during construction. - if (this._first) { - this._first = false; - return this._findData; - } - - if (WinFile.FindNextFile(this._handle, this._findDataPtr)) { - return this._findData; - } - let error = ctypes.winLastError; - this.close(); - if (error == Const.ERROR_NO_MORE_FILES) { - return null; - } - throw new File.Error("iter (FindNextFile)", error, this._path); - }; - - /** - * Return the next entry in the directory, if any such entry is - * available. - * - * Skip special directories "." and "..". - * - * @return By definition of the iterator protocol, either - * `{value: {File.Entry}, done: false}` if there is an unvisited entry - * in the directory, or `{value: undefined, done: true}`, otherwise. - */ - File.DirectoryIterator.prototype.next = function next() { - // FIXME: If we start supporting "\\?\"-prefixed paths, do not forget - // that "." and ".." are absolutely normal file names if _path starts - // with such prefix - for (let entry = this._next(); entry != null; entry = this._next()) { - let name = entry.cFileName.readString(); - if (name == "." || name == "..") { - continue; - } - return { - value: new File.DirectoryIterator.Entry(entry, this._path), - done: false, - }; - } - return { value: undefined, done: true }; - }; - - File.DirectoryIterator.prototype.close = function close() { - if (this._closed) { - return; - } - this._closed = true; - if (this._handle) { - // We might not have a handle if the iterator is closed - // before being used. - throw_on_zero("FindClose", WinFile.FindClose(this._handle), this._path); - this._handle = null; - } - }; - - /** - * Determine whether the directory exists. - * - * @return {boolean} - */ - File.DirectoryIterator.prototype.exists = function exists() { - return this._exists; - }; - - File.DirectoryIterator.Entry = function Entry(win_entry, parent) { - if ( - !win_entry.dwFileAttributes || - !win_entry.ftLastAccessTime || - !win_entry.ftLastWriteTime - ) { - throw new TypeError(); - } - - // Copy the relevant part of |win_entry| to ensure that - // our data is not overwritten prematurely. - let isDir = !!( - win_entry.dwFileAttributes & Const.FILE_ATTRIBUTE_DIRECTORY - ); - let isSymLink = !!( - win_entry.dwFileAttributes & Const.FILE_ATTRIBUTE_REPARSE_POINT - ); - - let winLastWriteDate = FILETIME_to_Date( - win_entry.ftLastWriteTime, - this._path - ); - let winLastAccessDate = FILETIME_to_Date( - win_entry.ftLastAccessTime, - this._path - ); - - let name = win_entry.cFileName.readString(); - if (!name) { - throw new TypeError("Empty name"); - } - - if (!parent) { - throw new TypeError("Empty parent"); - } - this._parent = parent; - - let path = Path.join(this._parent, name); - - SysAll.AbstractEntry.call( - this, - isDir, - isSymLink, - name, - winLastWriteDate, - winLastAccessDate, - path - ); - }; - File.DirectoryIterator.Entry.prototype = Object.create( - SysAll.AbstractEntry.prototype - ); - - /** - * Return a version of an instance of - * File.DirectoryIterator.Entry that can be sent from a worker - * thread to the main thread. Note that deserialization is - * asymmetric and returns an object with a different - * implementation. - */ - File.DirectoryIterator.Entry.toMsg = function toMsg(value) { - if (!(value instanceof File.DirectoryIterator.Entry)) { - throw new TypeError( - "parameter of " + - "File.DirectoryIterator.Entry.toMsg must be a " + - "File.DirectoryIterator.Entry" - ); - } - let serialized = {}; - for (let key in File.DirectoryIterator.Entry.prototype) { - serialized[key] = value[key]; - } - return serialized; - }; - - /** - * Information on a file. - * - * To obtain the latest information on a file, use |File.stat| - * (for an unopened file) or |File.prototype.stat| (for an - * already opened file). - * - * @constructor - */ - File.Info = function Info(stat, path) { - let isDir = !!(stat.dwFileAttributes & Const.FILE_ATTRIBUTE_DIRECTORY); - let isSymLink = !!( - stat.dwFileAttributes & Const.FILE_ATTRIBUTE_REPARSE_POINT - ); - - let lastAccessDate = FILETIME_to_Date(stat.ftLastAccessTime, this._path); - let lastWriteDate = FILETIME_to_Date(stat.ftLastWriteTime, this._path); - - let value = ctypes.UInt64.join(stat.nFileSizeHigh, stat.nFileSizeLow); - let size = Type.uint64_t.importFromC(value); - let winAttributes = { - readOnly: !!(stat.dwFileAttributes & Const.FILE_ATTRIBUTE_READONLY), - system: !!(stat.dwFileAttributes & Const.FILE_ATTRIBUTE_SYSTEM), - hidden: !!(stat.dwFileAttributes & Const.FILE_ATTRIBUTE_HIDDEN), - }; - - SysAll.AbstractInfo.call( - this, - path, - isDir, - isSymLink, - size, - lastAccessDate, - lastWriteDate, - winAttributes - ); - }; - File.Info.prototype = Object.create(SysAll.AbstractInfo.prototype); - - /** - * Return a version of an instance of File.Info that can be sent - * from a worker thread to the main thread. Note that deserialization - * is asymmetric and returns an object with a different implementation. - */ - File.Info.toMsg = function toMsg(stat) { - if (!(stat instanceof File.Info)) { - throw new TypeError("parameter of File.Info.toMsg must be a File.Info"); - } - let serialized = {}; - for (let key in File.Info.prototype) { - serialized[key] = stat[key]; - } - return serialized; - }; - - /** - * Fetch the information on a file. - * - * Performance note: if you have opened the file already, - * method |File.prototype.stat| is generally much faster - * than method |File.stat|. - * - * Platform-specific note: under Windows, if the file is - * already opened without sharing of the read capability, - * this function will fail. - * - * @return {File.Information} - */ - File.stat = function stat(path) { - let file = File.open(path, FILE_STAT_MODE, FILE_STAT_OPTIONS); - try { - return file.stat(); - } finally { - file.close(); - } - }; - // All of the following is required to ensure that File.stat - // also works on directories. - const FILE_STAT_MODE = { - read: true, - }; - const FILE_STAT_OPTIONS = { - // Directories can be opened neither for reading(!) nor for writing - winAccess: 0, - // Directories can only be opened with backup semantics(!) - winFlags: Const.FILE_FLAG_BACKUP_SEMANTICS, - winDisposition: Const.OPEN_EXISTING, - }; - - /** - * Set the file's access permission bits. - */ - File.setPermissions = function setPermissions(path, options = {}) { - if (!("winAttributes" in options)) { - return; - } - let oldAttributes = WinFile.GetFileAttributes(path); - if (oldAttributes == Const.INVALID_FILE_ATTRIBUTES) { - throw new File.Error("setPermissions", ctypes.winLastError, path); - } - let newAttributes = toFileAttributes( - options.winAttributes, - oldAttributes - ); - throw_on_zero( - "setPermissions", - WinFile.SetFileAttributes(path, newAttributes), - path - ); - }; - - /** - * Set the last access and modification date of the file. - * The time stamp resolution is 1 second at best, but might be worse - * depending on the platform. - * - * Performance note: if you have opened the file already in write mode, - * method |File.prototype.stat| is generally much faster - * than method |File.stat|. - * - * Platform-specific note: under Windows, if the file is - * already opened without sharing of the write capability, - * this function will fail. - * - * @param {string} path The full name of the file to set the dates for. - * @param {Date,number=} accessDate The last access date. If numeric, - * milliseconds since epoch. If omitted or null, then the current date - * will be used. - * @param {Date,number=} modificationDate The last modification date. If - * numeric, milliseconds since epoch. If omitted or null, then the current - * date will be used. - * - * @throws {TypeError} In case of invalid paramters. - * @throws {OS.File.Error} In case of I/O error. - */ - File.setDates = function setDates(path, accessDate, modificationDate) { - let file = File.open(path, FILE_SETDATES_MODE, FILE_SETDATES_OPTIONS); - try { - return file.setDates(accessDate, modificationDate); - } finally { - file.close(); - } - }; - // All of the following is required to ensure that File.setDates - // also works on directories. - const FILE_SETDATES_MODE = { - write: true, - }; - const FILE_SETDATES_OPTIONS = { - winAccess: Const.GENERIC_WRITE, - // Directories can only be opened with backup semantics(!) - winFlags: Const.FILE_FLAG_BACKUP_SEMANTICS, - winDisposition: Const.OPEN_EXISTING, - }; - - File.read = exports.OS.Shared.AbstractFile.read; - File.writeAtomic = exports.OS.Shared.AbstractFile.writeAtomic; - File.openUnique = exports.OS.Shared.AbstractFile.openUnique; - File.makeDir = exports.OS.Shared.AbstractFile.makeDir; - - /** - * Remove an existing directory and its contents. - * - * @param {string} path The name of the directory. - * @param {*=} options Additional options. - * - {bool} ignoreAbsent If |false|, throw an error if the directory doesn't - * exist. |true| by default. - * - {boolean} ignorePermissions If |true|, remove the file even when lacking write - * permission. - * - * @throws {OS.File.Error} In case of I/O error, in particular if |path| is - * not a directory. - */ - File.removeDir = function(path, options = {}) { - // We can't use File.stat here because it will follow the symlink. - let attributes = WinFile.GetFileAttributes(path); - if (attributes == Const.INVALID_FILE_ATTRIBUTES) { - if ( - (!("ignoreAbsent" in options) || options.ignoreAbsent) && - ctypes.winLastError == Const.ERROR_FILE_NOT_FOUND - ) { - return; - } - throw new File.Error("removeEmptyDir", ctypes.winLastError, path); - } - if (attributes & Const.FILE_ATTRIBUTE_REPARSE_POINT) { - // Unlike Unix symlinks, NTFS junctions or NTFS symlinks to - // directories are directories themselves. OS.File.remove() - // will not work for them. - OS.File.removeEmptyDir(path, options); - return; - } - exports.OS.Shared.AbstractFile.removeRecursive(path, options); - }; - - /** - * Get the current directory by getCurrentDirectory. - */ - File.getCurrentDirectory = function getCurrentDirectory() { - // This function is more complicated than one could hope. - // - // This is due to two facts: - // - the maximal length of a path under Windows is not completely - // specified (there is a constant MAX_PATH, but it is quite possible - // to create paths that are much larger, see bug 744413); - // - if we attempt to call |GetCurrentDirectory| with a buffer that - // is too short, it returns the length of the current directory, but - // this length might be insufficient by the time we can call again - // the function with a larger buffer, in the (unlikely but possible) - // case in which the process changes directory to a directory with - // a longer name between both calls. - // - let buffer_size = 4096; - while (true) { - let array = new (ctypes.ArrayType(ctypes.char16_t, buffer_size))(); - let expected_size = throw_on_zero( - "getCurrentDirectory", - WinFile.GetCurrentDirectory(buffer_size, array) - ); - if (expected_size <= buffer_size) { - return array.readString(); - } - // At this point, we are in a case in which our buffer was not - // large enough to hold the name of the current directory. - // Consequently, we need to increase the size of the buffer. - // Note that, even in crazy scenarios, the loop will eventually - // converge, as the length of the paths cannot increase infinitely. - buffer_size = expected_size + 1 /* to store \0 */; - } - }; - - /** - * Get/set the current directory by |curDir|. - */ - Object.defineProperty(File, "curDir", { - set(path) { - this.setCurrentDirectory(path); - }, - get() { - return this.getCurrentDirectory(); - }, - }); - - // Utility functions, used for error-handling - - /** - * Turn the result of |open| into an Error or a File - * @param {number} maybe The result of the |open| operation that may - * represent either an error or a success. If -1, this function raises - * an error holding ctypes.winLastError, otherwise it returns the opened file. - * @param {string=} path The path of the file. - */ - function error_or_file(maybe, path) { - if (maybe == Const.INVALID_HANDLE_VALUE) { - throw new File.Error("open", ctypes.winLastError, path); - } - return new File(maybe, path); - } - - /** - * Utility function to sort errors represented as "0" from successes. - * - * @param {string=} operation The name of the operation. If unspecified, - * the name of the caller function. - * @param {number} result The result of the operation that may - * represent either an error or a success. If 0, this function raises - * an error holding ctypes.winLastError, otherwise it returns |result|. - * @param {string=} path The path of the file. - */ - function throw_on_zero(operation, result, path) { - if (result == 0) { - throw new File.Error(operation, ctypes.winLastError, path); - } - return result; - } - - /** - * Helper used by both versions of setPermissions - */ - function toFileAttributes(winAttributes, oldDwAttrs) { - if ("readOnly" in winAttributes) { - if (winAttributes.readOnly) { - oldDwAttrs |= Const.FILE_ATTRIBUTE_READONLY; - } else { - oldDwAttrs &= ~Const.FILE_ATTRIBUTE_READONLY; - } - } - if ("system" in winAttributes) { - if (winAttributes.system) { - oldDwAttrs |= Const.FILE_ATTRIBUTE_SYSTEM; - } else { - oldDwAttrs &= ~Const.FILE_ATTRIBUTE_SYSTEM; - } - } - if ("hidden" in winAttributes) { - if (winAttributes.hidden) { - oldDwAttrs |= Const.FILE_ATTRIBUTE_HIDDEN; - } else { - oldDwAttrs &= ~Const.FILE_ATTRIBUTE_HIDDEN; - } - } - return oldDwAttrs; - } - - File.Win = exports.OS.Win.File; - File.Error = SysAll.Error; - exports.OS.File = File; - exports.OS.Shared.Type = Type; - - Object.defineProperty(File, "POS_START", { value: SysAll.POS_START }); - Object.defineProperty(File, "POS_CURRENT", { value: SysAll.POS_CURRENT }); - Object.defineProperty(File, "POS_END", { value: SysAll.POS_END }); - })(this); -}
deleted file mode 100644 --- a/toolkit/components/osfile/modules/ospath.jsm +++ /dev/null @@ -1,50 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Handling native paths. - * - * This module contains a number of functions destined to simplify - * working with native paths through a cross-platform API. Functions - * of this module will only work with the following assumptions: - * - * - paths are valid; - * - paths are defined with one of the grammars that this module can - * parse (see later); - * - all path concatenations go through function |join|. - */ - -/* global OS */ -/* eslint-env node */ - -"use strict"; - -if (typeof Components == "undefined") { - let Path; - if (OS.Constants.Win) { - Path = require("resource://gre/modules/osfile/ospath_win.jsm"); - } else { - Path = require("resource://gre/modules/osfile/ospath_unix.jsm"); - } - module.exports = Path; -} else { - let Scope = ChromeUtils.import( - "resource://gre/modules/osfile/osfile_shared_allthreads.jsm" - ); - - let Path; - if (Scope.OS.Constants.Win) { - Path = ChromeUtils.import("resource://gre/modules/osfile/ospath_win.jsm"); - } else { - Path = ChromeUtils.import("resource://gre/modules/osfile/ospath_unix.jsm"); - } - - // eslint-disable-next-line mozilla/reject-global-this - this.EXPORTED_SYMBOLS = []; - for (let k in Path) { - EXPORTED_SYMBOLS.push(k); - // eslint-disable-next-line mozilla/reject-global-this - this[k] = Path[k]; - } -}
deleted file mode 100644 --- a/toolkit/components/osfile/modules/ospath_unix.jsm +++ /dev/null @@ -1,204 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Handling native paths. - * - * This module contains a number of functions destined to simplify - * working with native paths through a cross-platform API. Functions - * of this module will only work with the following assumptions: - * - * - paths are valid; - * - paths are defined with one of the grammars that this module can - * parse (see later); - * - all path concatenations go through function |join|. - */ - -"use strict"; - -// Boilerplate used to be able to import this module both from the main -// thread and from worker threads. -if (typeof Components != "undefined") { - // Global definition of |exports|, to keep everybody happy. - // In non-main thread, |exports| is provided by the module - // loader. - // eslint-disable-next-line mozilla/reject-global-this - this.exports = {}; -} else if (typeof module == "undefined" || typeof exports == "undefined") { - throw new Error("Please load this module using require()"); -} - -var EXPORTED_SYMBOLS = [ - "basename", - "dirname", - "join", - "normalize", - "split", - "toFileURI", - "fromFileURI", -]; - -/** - * Return the final part of the path. - * The final part of the path is everything after the last "/". - */ -var basename = function(path) { - return path.slice(path.lastIndexOf("/") + 1); -}; -exports.basename = basename; - -/** - * Return the directory part of the path. - * The directory part of the path is everything before the last - * "/". If the last few characters of this part are also "/", - * they are ignored. - * - * If the path contains no directory, return ".". - */ -var dirname = function(path) { - let index = path.lastIndexOf("/"); - if (index == -1) { - return "."; - } - while (index >= 0 && path[index] == "/") { - --index; - } - return path.slice(0, index + 1); -}; -exports.dirname = dirname; - -/** - * Join path components. - * This is the recommended manner of getting the path of a file/subdirectory - * in a directory. - * - * Example: Obtaining $TMP/foo/bar in an OS-independent manner - * var tmpDir = OS.Constants.Path.tmpDir; - * var path = OS.Path.join(tmpDir, "foo", "bar"); - * - * Under Unix, this will return "/tmp/foo/bar". - * - * Empty components are ignored, i.e. `OS.Path.join("foo", "", "bar)` is the - * same as `OS.Path.join("foo", "bar")`. - */ -var join = function(...path) { - // If there is a path that starts with a "/", eliminate everything before - let paths = []; - for (let subpath of path) { - if (subpath == null) { - throw new TypeError("invalid path component"); - } - if (!subpath.length) { - continue; - } else if (subpath[0] == "/") { - paths = [subpath]; - } else { - paths.push(subpath); - } - } - return paths.join("/"); -}; -exports.join = join; - -/** - * Normalize a path by removing any unneeded ".", "..", "//". - */ -var normalize = function(path) { - let stack = []; - let absolute; - if (path.length >= 0 && path[0] == "/") { - absolute = true; - } else { - absolute = false; - } - path.split("/").forEach(function(v) { - switch (v) { - case "": - case ".": // fallthrough - break; - case "..": - if (!stack.length) { - if (absolute) { - throw new Error("Path is ill-formed: attempting to go past root"); - } else { - stack.push(".."); - } - } else if (stack[stack.length - 1] == "..") { - stack.push(".."); - } else { - stack.pop(); - } - break; - default: - stack.push(v); - } - }); - let string = stack.join("/"); - return absolute ? "/" + string : string; -}; -exports.normalize = normalize; - -/** - * Return the components of a path. - * You should generally apply this function to a normalized path. - * - * @return {{ - * {bool} absolute |true| if the path is absolute, |false| otherwise - * {array} components the string components of the path - * }} - * - * Other implementations may add additional OS-specific informations. - */ -var split = function(path) { - return { - absolute: path.length && path[0] == "/", - components: path.split("/"), - }; -}; -exports.split = split; - -/** - * Returns the file:// URI file path of the given local file path. - */ -// The case of %3b is designed to match Services.io, but fundamentally doesn't matter. -var toFileURIExtraEncodings = { ";": "%3b", "?": "%3F", "#": "%23" }; -var toFileURI = function toFileURI(path) { - // Per https://url.spec.whatwg.org we should not encode [] in the path - let dontNeedEscaping = { "%5B": "[", "%5D": "]" }; - let uri = encodeURI(this.normalize(path)).replace( - /%(5B|5D)/gi, - match => dontNeedEscaping[match] - ); - - // add a prefix, and encodeURI doesn't escape a few characters that we do - // want to escape, so fix that up - let prefix = "file://"; - uri = prefix + uri.replace(/[;?#]/g, match => toFileURIExtraEncodings[match]); - - return uri; -}; -exports.toFileURI = toFileURI; - -/** - * Returns the local file path from a given file URI. - */ -var fromFileURI = function fromFileURI(uri) { - let url = new URL(uri); - if (url.protocol != "file:") { - throw new Error("fromFileURI expects a file URI"); - } - let path = this.normalize(decodeURIComponent(url.pathname)); - return path; -}; -exports.fromFileURI = fromFileURI; - -// ////////// Boilerplate -if (typeof Components != "undefined") { - // eslint-disable-next-line mozilla/reject-global-this - this.EXPORTED_SYMBOLS = EXPORTED_SYMBOLS; - for (let symbol of EXPORTED_SYMBOLS) { - // eslint-disable-next-line mozilla/reject-global-this - this[symbol] = exports[symbol]; - } -}
deleted file mode 100644 --- a/toolkit/components/osfile/modules/ospath_win.jsm +++ /dev/null @@ -1,382 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Handling native paths. - * - * This module contains a number of functions destined to simplify - * working with native paths through a cross-platform API. Functions - * of this module will only work with the following assumptions: - * - * - paths are valid; - * - paths are defined with one of the grammars that this module can - * parse (see later); - * - all path concatenations go through function |join|. - * - * Limitations of this implementation. - * - * Windows supports 6 distinct grammars for paths. For the moment, this - * implementation supports the following subset: - * - * - drivename:backslash-separated components - * - backslash-separated components - * - \\drivename\ followed by backslash-separated components - * - * Additionally, |normalize| can convert a path containing slash- - * separated components to a path containing backslash-separated - * components. - */ - -"use strict"; - -// Boilerplate used to be able to import this module both from the main -// thread and from worker threads. -if (typeof Components != "undefined") { - // Global definition of |exports|, to keep everybody happy. - // In non-main thread, |exports| is provided by the module - // loader. - // eslint-disable-next-line mozilla/reject-global-this - this.exports = {}; -} else if (typeof module == "undefined" || typeof exports == "undefined") { - throw new Error("Please load this module using require()"); -} - -var EXPORTED_SYMBOLS = [ - "basename", - "dirname", - "join", - "normalize", - "split", - "winGetDrive", - "winIsAbsolute", - "toFileURI", - "fromFileURI", -]; - -/** - * Return the final part of the path. - * The final part of the path is everything after the last "\\". - */ -var basename = function(path) { - if (path.startsWith("\\\\")) { - // UNC-style path - let index = path.lastIndexOf("\\"); - if (index != 1) { - return path.slice(index + 1); - } - return ""; // Degenerate case - } - return path.slice( - Math.max(path.lastIndexOf("\\"), path.lastIndexOf(":")) + 1 - ); -}; -exports.basename = basename; - -/** - * Return the directory part of the path. - * - * If the path contains no directory, return the drive letter, - * or "." if the path contains no drive letter or if option - * |winNoDrive| is set. - * - * Otherwise, return everything before the last backslash, - * including the drive/server name. - * - * - * @param {string} path The path. - * @param {*=} options Platform-specific options controlling the behavior - * of this function. This implementation supports the following options: - * - |winNoDrive| If |true|, also remove the letter from the path name. - */ -var dirname = function(path, options) { - let noDrive = options && options.winNoDrive; - - // Find the last occurrence of "\\" - let index = path.lastIndexOf("\\"); - if (index == -1) { - // If there is no directory component... - if (!noDrive) { - // Return the drive path if possible, falling back to "." - return this.winGetDrive(path) || "."; - } - // Or just "." - return "."; - } - - if (index == 1 && path.charAt(0) == "\\") { - // The path is reduced to a UNC drive - if (noDrive) { - return "."; - } - return path; - } - - // Ignore any occurrence of "\\: immediately before that one - while (index >= 0 && path[index] == "\\") { - --index; - } - - // Compute what is left, removing the drive name if necessary - let start; - if (noDrive) { - start = (this.winGetDrive(path) || "").length; - } else { - start = 0; - } - return path.slice(start, index + 1); -}; -exports.dirname = dirname; - -/** - * Join path components. - * This is the recommended manner of getting the path of a file/subdirectory - * in a directory. - * - * Example: Obtaining $TMP/foo/bar in an OS-independent manner - * var tmpDir = OS.Constants.Path.tmpDir; - * var path = OS.Path.join(tmpDir, "foo", "bar"); - * - * Under Windows, this will return "$TMP\foo\bar". - * - * Empty components are ignored, i.e. `OS.Path.join("foo", "", "bar)` is the - * same as `OS.Path.join("foo", "bar")`. - */ -var join = function(...path) { - let paths = []; - let root; - let absolute = false; - for (let subpath of path) { - if (subpath == null) { - throw new TypeError("invalid path component"); - } - if (subpath == "") { - continue; - } - let drive = this.winGetDrive(subpath); - if (drive) { - root = drive; - let component = trimBackslashes(subpath.slice(drive.length)); - if (component) { - paths = [component]; - } else { - paths = []; - } - absolute = true; - } else if (this.winIsAbsolute(subpath)) { - paths = [trimBackslashes(subpath)]; - absolute = true; - } else { - paths.push(trimBackslashes(subpath)); - } - } - let result = ""; - if (root) { - result += root; - } - if (absolute) { - result += "\\"; - } - result += paths.join("\\"); - return result; -}; -exports.join = join; - -/** - * Return the drive name of a path, or |null| if the path does - * not contain a drive name. - * - * Drive name appear either as "DriveName:..." (the return drive - * name includes the ":") or "\\\\DriveName..." (the returned drive name - * includes "\\\\"). - */ -var winGetDrive = function(path) { - if (path == null) { - throw new TypeError("path is invalid"); - } - - if (path.startsWith("\\\\")) { - // UNC path - if (path.length == 2) { - return null; - } - let index = path.indexOf("\\", 2); - if (index == -1) { - return path; - } - return path.slice(0, index); - } - // Non-UNC path - let index = path.indexOf(":"); - if (index <= 0) { - return null; - } - return path.slice(0, index + 1); -}; -exports.winGetDrive = winGetDrive; - -/** - * Return |true| if the path is absolute, |false| otherwise. - * - * We consider that a path is absolute if it starts with "\\" - * or "driveletter:\\". - */ -var winIsAbsolute = function(path) { - let index = path.indexOf(":"); - return path.length > index + 1 && path[index + 1] == "\\"; -}; -exports.winIsAbsolute = winIsAbsolute; - -/** - * Normalize a path by removing any unneeded ".", "..", "\\". - * Also convert any "/" to a "\\". - */ -var normalize = function(path) { - let stack = []; - - if (!path.startsWith("\\\\")) { - // Normalize "/" to "\\" - path = path.replace(/\//g, "\\"); - } - - // Remove the drive (we will put it back at the end) - let root = this.winGetDrive(path); - if (root) { - path = path.slice(root.length); - } - - // Remember whether we need to restore a leading "\\" or drive name. - let absolute = this.winIsAbsolute(path); - - // And now, fill |stack| from the components, - // popping whenever there is a ".." - path.split("\\").forEach(function loop(v) { - switch (v) { - case "": - case ".": // Ignore - break; - case "..": - if (!stack.length) { - if (absolute) { - throw new Error("Path is ill-formed: attempting to go past root"); - } else { - stack.push(".."); - } - } else if (stack[stack.length - 1] == "..") { - stack.push(".."); - } else { - stack.pop(); - } - break; - default: - stack.push(v); - } - }); - - // Put everything back together - let result = stack.join("\\"); - if (absolute || root) { - result = "\\" + result; - } - if (root) { - result = root + result; - } - return result; -}; -exports.normalize = normalize; - -/** - * Return the components of a path. - * You should generally apply this function to a normalized path. - * - * @return {{ - * {bool} absolute |true| if the path is absolute, |false| otherwise - * {array} components the string components of the path - * {string?} winDrive the drive or server for this path - * }} - * - * Other implementations may add additional OS-specific informations. - */ -var split = function(path) { - return { - absolute: this.winIsAbsolute(path), - winDrive: this.winGetDrive(path), - components: path.split("\\"), - }; -}; -exports.split = split; - -/** - * Return the file:// URI file path of the given local file path. - */ -// The case of %3b is designed to match Services.io, but fundamentally doesn't matter. -var toFileURIExtraEncodings = { ";": "%3b", "?": "%3F", "#": "%23" }; -var toFileURI = function toFileURI(path) { - // URI-escape forward slashes and convert backward slashes to forward - path = this.normalize(path).replace(/[\\\/]/g, m => - m == "\\" ? "/" : "%2F" - ); - // Per https://url.spec.whatwg.org we should not encode [] in the path - let dontNeedEscaping = { "%5B": "[", "%5D": "]" }; - let uri = encodeURI(path).replace( - /%(5B|5D)/gi, - match => dontNeedEscaping[match] - ); - - // add a prefix, and encodeURI doesn't escape a few characters that we do - // want to escape, so fix that up - let prefix = "file:///"; - uri = prefix + uri.replace(/[;?#]/g, match => toFileURIExtraEncodings[match]); - - // turn e.g., file:///C: into file:///C:/ - if (uri.charAt(uri.length - 1) === ":") { - uri += "/"; - } - - return uri; -}; -exports.toFileURI = toFileURI; - -/** - * Returns the local file path from a given file URI. - */ -var fromFileURI = function fromFileURI(uri) { - let url = new URL(uri); - if (url.protocol != "file:") { - throw new Error("fromFileURI expects a file URI"); - } - - // strip leading slash, since Windows paths don't start with one - uri = url.pathname.substr(1); - - let path = decodeURI(uri); - // decode a few characters where URL's parsing is overzealous - path = path.replace(/%(3b|3f|23)/gi, match => decodeURIComponent(match)); - path = this.normalize(path); - - // this.normalize() does not remove the trailing slash if the path - // component is a drive letter. eg. 'C:\'' will not get normalized. - if (path.endsWith(":\\")) { - path = path.substr(0, path.length - 1); - } - return this.normalize(path); -}; -exports.fromFileURI = fromFileURI; - -/** - * Utility function: Remove any leading/trailing backslashes - * from a string. - */ -var trimBackslashes = function trimBackslashes(string) { - return string.replace(/^\\+|\\+$/g, ""); -}; - -// ////////// Boilerplate -if (typeof Components != "undefined") { - // eslint-disable-next-line mozilla/reject-global-this - this.EXPORTED_SYMBOLS = EXPORTED_SYMBOLS; - for (let symbol of EXPORTED_SYMBOLS) { - // eslint-disable-next-line mozilla/reject-global-this - this[symbol] = exports[symbol]; - } -}
deleted file mode 100644 --- a/toolkit/components/osfile/moz.build +++ /dev/null @@ -1,35 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -DIRS += [ - "modules", -] - -MOCHITEST_CHROME_MANIFESTS += ["tests/mochi/chrome.ini"] -XPCSHELL_TESTS_MANIFESTS += ["tests/xpcshell/xpcshell.ini"] - -SOURCES += [ - "NativeOSFileInternals.cpp", -] - -XPIDL_MODULE = "toolkit_osfile" - -XPIDL_SOURCES += [ - "nsINativeOSFileInternals.idl", -] - -EXPORTS.mozilla += [ - "NativeOSFileInternals.h", -] - -EXTRA_JS_MODULES += [ - "osfile.jsm", -] - -FINAL_LIBRARY = "xul" - -with Files("**"): - BUG_COMPONENT = ("Toolkit", "OS.File")
deleted file mode 100644 --- a/toolkit/components/osfile/nsINativeOSFileInternals.idl +++ /dev/null @@ -1,120 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* vim: set ts=2 et sw=2 tw=40: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" - -/** - * The result of a successful asynchronous operation. - */ -[scriptable, builtinclass, uuid(08B4CF29-3D65-4E79-B522-A694C322ED07)] -interface nsINativeOSFileResult: nsISupports -{ - /** - * The actual value produced by the operation. - * - * Actual type of this value depends on the options passed to the - * operation. - */ - [implicit_jscontext] - readonly attribute jsval result; - - /** - * Delay between when the operation was requested on the main thread and - * when the operation was started off main thread. - */ - readonly attribute double dispatchDurationMS; - - /** - * Duration of the off main thread execution. - */ - readonly attribute double executionDurationMS; -}; - -/** - * A callback invoked in case of success. - */ -[scriptable, function, uuid(2C1922CA-CA1B-4099-8B61-EC23CFF49412)] -interface nsINativeOSFileSuccessCallback: nsISupports -{ - void complete(in nsINativeOSFileResult result); -}; - -/** - * A callback invoked in case of error. - */ -[scriptable, function, uuid(F612E0FC-6736-4D24-AA50-FD661B3B40B6)] -interface nsINativeOSFileErrorCallback: nsISupports -{ - /** - * @param operation The name of the failed operation. Provided to aid - * debugging only, may change without notice. - * @param OSstatus The OS status of the operation (errno under Unix, - * GetLastError under Windows). - */ - void complete(in ACString operation, in long OSstatus); -}; - -/** - * A service providing native implementations of some of the features - * of OS.File. - */ -[scriptable, builtinclass, uuid(913362AD-1526-4623-9E6B-A2EB08AFBBB9)] -interface nsINativeOSFileInternalsService: nsISupports -{ - /** - * Implementation of OS.File.read - * - * @param path The absolute path to the file to read. - * @param options An object that may contain some of the following fields - * - {number} bytes The maximal number of bytes to read. - * - {string} encoding If provided, return the result as a string, decoded - * using this encoding. Otherwise, pass the result as an ArrayBuffer. - * Invalid encodings cause onError to be called with the platform-specific - * "invalid argument" constant. - * - {string} compression Unimplemented at the moment. - * @param onSuccess The success callback. - * @param onError The error callback. - */ - [implicit_jscontext] - void read(in AString path, in jsval options, - in nsINativeOSFileSuccessCallback onSuccess, - in nsINativeOSFileErrorCallback onError); - - /** - * Implementation of OS.File.writeAtomic - * - * @param path the absolute path of the file to write to. - * @param buffer the data as an array buffer to be written to the file. - * @param options An object that may contain the following fields - * - {number} bytes If provided, the number of bytes written is equal to this. - * The default value is the size of the |buffer|. - * - {string} tmpPath If provided and not null, first write to this path, and - * move to |path| after writing. - * - {string} backupPath if provided, backup file at |path| to this path - * before overwriting it. - * - {bool} flush if provided and true, flush the contents of the buffer after - * writing. This is slower, but safer. - * - {bool} noOverwrite if provided and true, do not write if a file already - * exists at |path|. - * @param onSuccess The success callback. - * @param onError The error callback. - */ - [implicit_jscontext] - void writeAtomic(in AString path, - in jsval buffer, - in jsval options, - in nsINativeOSFileSuccessCallback onSuccess, - in nsINativeOSFileErrorCallback onError); - -}; - - -%{ C++ - -#define NATIVE_OSFILE_INTERNALS_SERVICE_CID {0x63A69303,0x8A64,0x45A9,{0x84, 0x8C, 0xD4, 0xE2, 0x79, 0x27, 0x94, 0xE6}} -#define NATIVE_OSFILE_INTERNALS_SERVICE_CONTRACTID "@mozilla.org/toolkit/osfile/native-internals;1" - -%}
deleted file mode 100644 --- a/toolkit/components/osfile/osfile.jsm +++ /dev/null @@ -1,46 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Common front for various implementations of OS.File - */ - -if (typeof Components != "undefined") { - // eslint-disable-next-line mozilla/reject-global-this - this.EXPORTED_SYMBOLS = ["OS"]; - const { OS } = ChromeUtils.import( - "resource://gre/modules/osfile/osfile_async_front.jsm" - ); - // eslint-disable-next-line mozilla/reject-global-this - this.OS = OS; -} else { - /* eslint-env worker */ - /* import-globals-from /toolkit/components/workerloader/require.js */ - importScripts("resource://gre/modules/workers/require.js"); - - var SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm"); - - /* eslint-disable no-unused-vars */ - - // At this stage, we need to import all sources at once to avoid - // a unique failure on tbpl + talos that seems caused by a - // what looks like a nested event loop bug (see bug 794091). - if (SharedAll.Constants.Win) { - importScripts( - "resource://gre/modules/osfile/osfile_win_back.js", - "resource://gre/modules/osfile/osfile_shared_front.js", - "resource://gre/modules/osfile/osfile_win_front.js" - ); - } else { - importScripts( - "resource://gre/modules/osfile/osfile_unix_back.js", - "resource://gre/modules/osfile/osfile_shared_front.js", - "resource://gre/modules/osfile/osfile_unix_front.js" - ); - } - - /* eslint-enable no-unused-vars */ - - OS.Path = require("resource://gre/modules/osfile/ospath.jsm"); -}
deleted file mode 100644 --- a/toolkit/components/osfile/tests/mochi/chrome.ini +++ /dev/null @@ -1,15 +0,0 @@ -[DEFAULT] -skip-if = os == 'android' -support-files = - main_test_osfile_async.js - worker_test_osfile_comms.js - worker_test_osfile_front.js - worker_test_osfile_unix.js - worker_test_osfile_win.js - -[test_osfile_async.xhtml] -[test_osfile_back.xhtml] -[test_osfile_comms.xhtml] -[test_osfile_front.xhtml] -skip-if = - win11_2009 && bits == 32 # Bug 1809355
deleted file mode 100644 --- a/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js +++ /dev/null @@ -1,501 +0,0 @@ -"use strict"; - -const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -// The following are used to compare against a well-tested reference -// implementation of file I/O. -const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); -const { FileUtils } = ChromeUtils.importESModule( - "resource://gre/modules/FileUtils.sys.mjs" -); - -var myok = ok; -var myis = is; -var myinfo = info; -var myisnot = isnot; - -var isPromise = function ispromise(value) { - return value != null && typeof value == "object" && "then" in value; -}; - -var maketest = function(prefix, test) { - let utils = { - ok: function ok(t, m) { - myok(t, prefix + ": " + m); - }, - is: function is(l, r, m) { - myis(l, r, prefix + ": " + m); - }, - isnot: function isnot(l, r, m) { - myisnot(l, r, prefix + ": " + m); - }, - info: function info(m) { - myinfo(prefix + ": " + m); - }, - fail: function fail(m) { - utils.ok(false, m); - }, - okpromise: function okpromise(t, m) { - return t.then( - function onSuccess() { - utils.ok(true, m); - }, - function onFailure() { - utils.ok(false, m); - } - ); - }, - }; - return function runtest() { - utils.info("Entering"); - try { - let result = test.call(this, utils); - if (!isPromise(result)) { - throw new TypeError("The test did not return a promise"); - } - utils.info("This was a promise"); - // The test returns a promise - result = result.then( - function test_complete() { - utils.info("Complete"); - }, - function catch_uncaught_errors(err) { - utils.fail("Uncaught error " + err); - if (err && typeof err == "object" && "message" in err) { - utils.fail("(" + err.message + ")"); - } - if (err && typeof err == "object" && "stack" in err) { - utils.fail("at " + err.stack); - } - } - ); - return result; - } catch (x) { - utils.fail("Error " + x + " at " + x.stack); - return null; - } - }; -}; - -/** - * Fetch asynchronously the contents of a file using xpcom. - * - * Used for comparing xpcom-based results to os.file-based results. - * - * @param {string} path The _absolute_ path to the file. - * @return {promise} - * @resolves {string} The contents of the file. - */ -var reference_fetch_file = function reference_fetch_file(path, test) { - test.info("Fetching file " + path); - return new Promise((resolve, reject) => { - let file = new FileUtils.File(path); - NetUtil.asyncFetch( - { - uri: NetUtil.newURI(file), - loadUsingSystemPrincipal: true, - }, - function(stream, status) { - if (!Components.isSuccessCode(status)) { - reject(status); - return; - } - let result, reject; - try { - result = NetUtil.readInputStreamToString(stream, stream.available()); - } catch (x) { - reject = x; - } - stream.close(); - if (reject) { - reject(reject); - } else { - resolve(result); - } - } - ); - }); -}; - -var reference_dir_contents = function reference_dir_contents(path) { - let result = []; - let entries = new FileUtils.File(path).directoryEntries; - while (entries.hasMoreElements()) { - let entry = entries.nextFile; - result.push(entry.path); - } - return result; -}; - -// Set/Unset OS.Shared.DEBUG, OS.Shared.TEST and a console listener. -function toggleDebugTest(pref, consoleListener) { - Services.prefs.setBoolPref("toolkit.osfile.log", pref); - Services.prefs.setBoolPref("toolkit.osfile.log.redirect", pref); - Services.console[pref ? "registerListener" : "unregisterListener"]( - consoleListener - ); -} - -var test = maketest("Main", function main(test) { - return (async function() { - SimpleTest.waitForExplicitFinish(); - await test_stat(); - await test_debug(); - await test_info_features_detect(); - await test_position(); - await test_iter(); - await test_exists(); - await test_debug_test(); - info("Test is over"); - SimpleTest.finish(); - })(); -}); - -/** - * A file that we know exists and that can be used for reading. - */ -var EXISTING_FILE = OS.Path.join( - "chrome", - "toolkit", - "components", - "osfile", - "tests", - "mochi", - "main_test_osfile_async.js" -); - -/** - * Test OS.File.stat and OS.File.prototype.stat - */ -var test_stat = maketest("stat", function stat(test) { - return (async function() { - // Open a file and stat it - let file = await OS.File.open(EXISTING_FILE); - let stat1; - - try { - test.info("Stating file"); - stat1 = await file.stat(); - test.ok(true, "stat has worked " + stat1); - test.ok(stat1, "stat is not empty"); - } finally { - await file.close(); - } - - // Stat the same file without opening it - test.info("Stating a file without opening it"); - let stat2 = await OS.File.stat(EXISTING_FILE); - test.ok(true, "stat 2 has worked " + stat2); - test.ok(stat2, "stat 2 is not empty"); - for (let key in stat2) { - test.is( - "" + stat1[key], - "" + stat2[key], - "Stat field " + key + "is the same" - ); - } - })(); -}); - -/** - * Test feature detection using OS.File.Info.prototype on main thread - */ -var test_info_features_detect = maketest( - "features_detect", - function features_detect(test) { - return (async function() { - if (!OS.Constants.Win && OS.Constants.libc) { - // see if unixGroup is defined - if ("unixGroup" in OS.File.Info.prototype) { - test.ok(true, "unixGroup is defined"); - } else { - test.fail("unixGroup is not defined though we are under Unix"); - } - } - })(); - } -); - -/** - * Test file.{getPosition, setPosition} - */ -var test_position = maketest("position", function position(test) { - return (async function() { - let file = await OS.File.open(EXISTING_FILE); - - try { - let view = await file.read(); - test.info("First batch of content read"); - let CHUNK_SIZE = 178; // An arbitrary number of bytes to read from the file - let pos = await file.getPosition(); - test.info("Obtained position"); - test.is(pos, view.byteLength, "getPosition returned the end of the file"); - pos = await file.setPosition(-CHUNK_SIZE, OS.File.POS_END); - test.info("Changed position"); - test.is( - pos, - view.byteLength - CHUNK_SIZE, - "setPosition returned the correct position" - ); - - let view2 = await file.read(); - test.info("Read the end of the file"); - for (let i = 0; i < CHUNK_SIZE; ++i) { - if (view2[i] != view[i + view.byteLength - CHUNK_SIZE]) { - test.is( - view2[i], - view[i], - "setPosition put us in the right position" - ); - } - } - } finally { - await file.close(); - } - })(); -}); - -/** - * Test OS.File.prototype.{DirectoryIterator} - */ -var test_iter = maketest("iter", function iter(test) { - return (async function() { - let currentDir = await OS.File.getCurrentDirectory(); - - // Trivial walks through the directory - test.info("Preparing iteration"); - let iterator = new OS.File.DirectoryIterator(currentDir); - let temporary_file_name = OS.Path.join( - currentDir, - "empty-temporary-file.tmp" - ); - try { - await OS.File.remove(temporary_file_name); - } catch (err) { - // Ignore errors removing file - } - let allFiles1 = await iterator.nextBatch(); - test.info("Obtained all files through nextBatch"); - test.isnot(allFiles1.length, 0, "There is at least one file"); - test.isnot(allFiles1[0].path, null, "Files have a path"); - - // Ensure that we have the same entries with |reference_dir_contents| - let referenceEntries = new Set(); - for (let entry of reference_dir_contents(currentDir)) { - referenceEntries.add(entry); - } - test.is( - referenceEntries.size, - allFiles1.length, - "All the entries in the directory have been listed" - ); - for (let entry of allFiles1) { - test.ok( - referenceEntries.has(entry.path), - "File " + entry.path + " effectively exists" - ); - // Ensure that we have correct isDir and isSymLink - // Current directory is {objdir}/_tests/testing/mochitest/, assume it has some dirs and symlinks. - var f = new FileUtils.File(entry.path); - test.is( - entry.isDir, - f.isDirectory(), - "Get file " + entry.path + " isDir correctly" - ); - test.is( - entry.isSymLink, - f.isSymlink(), - "Get file " + entry.path + " isSymLink correctly" - ); - } - - await iterator.close(); - test.info("Closed iterator"); - - test.info("Double closing DirectoryIterator"); - iterator = new OS.File.DirectoryIterator(currentDir); - await iterator.close(); - await iterator.close(); // double closing |DirectoryIterator| - test.ok(true, "|DirectoryIterator| was closed twice successfully"); - - let allFiles2 = []; - let i = 0; - iterator = new OS.File.DirectoryIterator(currentDir); - await iterator.forEach(function(entry, index) { - test.is(i++, index, "Getting the correct index"); - allFiles2.push(entry); - }); - test.info("Obtained all files through forEach"); - is( - allFiles1.length, - allFiles2.length, - "Both runs returned the same number of files" - ); - for (let i = 0; i < allFiles1.length; ++i) { - if (allFiles1[i].path != allFiles2[i].path) { - test.is( - allFiles1[i].path, - allFiles2[i].path, - "Both runs return the same files" - ); - break; - } - } - - // Testing batch iteration + whether an iteration can be stopped early - let BATCH_LENGTH = 10; - test.info("Getting some files through nextBatch"); - await iterator.close(); - - iterator = new OS.File.DirectoryIterator(currentDir); - let someFiles1 = await iterator.nextBatch(BATCH_LENGTH); - let someFiles2 = await iterator.nextBatch(BATCH_LENGTH); - await iterator.close(); - - iterator = new OS.File.DirectoryIterator(currentDir); - await iterator.forEach(function cb(entry, index, iterator) { - if (index < BATCH_LENGTH) { - test.is( - entry.path, - someFiles1[index].path, - "Both runs return the same files (part 1)" - ); - } else if (index < 2 * BATCH_LENGTH) { - test.is( - entry.path, - someFiles2[index - BATCH_LENGTH].path, - "Both runs return the same files (part 2)" - ); - } else if (index == 2 * BATCH_LENGTH) { - test.info("Attempting to stop asynchronous forEach"); - return iterator.close(); - } else { - test.fail("Can we stop an asynchronous forEach? " + index); - } - return null; - }); - await iterator.close(); - - // Ensuring that we find new files if they appear - let file = await OS.File.open(temporary_file_name, { write: true }); - file.close(); - iterator = new OS.File.DirectoryIterator(currentDir); - try { - let files = await iterator.nextBatch(); - is( - files.length, - allFiles1.length + 1, - "The directory iterator has noticed the new file" - ); - let exists = await iterator.exists(); - test.ok( - exists, - "After nextBatch, iterator detects that the directory exists" - ); - } finally { - await iterator.close(); - } - - // Ensuring that opening a non-existing directory fails consistently - // once iteration starts. - try { - iterator = null; - iterator = new OS.File.DirectoryIterator("/I do not exist"); - let exists = await iterator.exists(); - test.ok( - !exists, - "Before any iteration, iterator detects that the directory doesn't exist" - ); - let exn = null; - try { - await iterator.next(); - } catch (ex) { - if (ex instanceof OS.File.Error && ex.becauseNoSuchFile) { - exn = ex; - let exists = await iterator.exists(); - test.ok( - !exists, - "After one iteration, iterator detects that the directory doesn't exist" - ); - } else { - throw ex; - } - } - test.ok( - exn, - "Iterating through a directory that does not exist has failed with becauseNoSuchFile" - ); - } finally { - if (iterator) { - iterator.close(); - } - } - test.ok( - !!iterator, - "The directory iterator for a non-existing directory was correctly created" - ); - })(); -}); - -/** - * Test OS.File.prototype.{exists} - */ -var test_exists = maketest("exists", function exists(test) { - return (async function() { - let fileExists = await OS.File.exists(EXISTING_FILE); - test.ok(fileExists, "file exists"); - fileExists = await OS.File.exists(EXISTING_FILE + ".tmp"); - test.ok(!fileExists, "file does not exists"); - })(); -}); - -/** - * Test changes to OS.Shared.DEBUG flag. - */ -var test_debug = maketest("debug", function debug(test) { - return (async function() { - function testSetDebugPref(pref) { - try { - Services.prefs.setBoolPref("toolkit.osfile.log", pref); - } catch (x) { - test.fail( - "Setting OS.Shared.DEBUG to " + pref + " should not cause error." - ); - } finally { - test.is(OS.Shared.DEBUG, pref, "OS.Shared.DEBUG is set correctly."); - } - } - testSetDebugPref(true); - let workerDEBUG = await OS.File.GET_DEBUG(); - test.is(workerDEBUG, true, "Worker's DEBUG is set."); - testSetDebugPref(false); - workerDEBUG = await OS.File.GET_DEBUG(); - test.is(workerDEBUG, false, "Worker's DEBUG is unset."); - })(); -}); - -/** - * Test logging in the main thread with set OS.Shared.DEBUG and - * OS.Shared.TEST flags. - */ -var test_debug_test = maketest("debug_test", function debug_test(test) { - return (async function() { - // Create a console listener. - let consoleListener = { - observe(aMessage) { - // Ignore unexpected messages. - if (!(aMessage instanceof Ci.nsIConsoleMessage)) { - return; - } - if (!aMessage.message.includes("TEST OS")) { - return; - } - test.ok(true, "DEBUG TEST messages are logged correctly."); - }, - }; - toggleDebugTest(true, consoleListener); - // Execution of OS.File.exist method will trigger OS.File.LOG several times. - await OS.File.exists(EXISTING_FILE); - toggleDebugTest(false, consoleListener); - })(); -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/mochi/test_osfile_async.xhtml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0"?> -<!-- - Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ ---> -<window title="Testing async I/O with OS.File" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - onload="test();"> - - <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> - <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/> - <script type="application/javascript" - src="main_test_osfile_async.js"/> - - <body xmlns="http://www.w3.org/1999/xhtml"> - <p id="display"></p> - <div id="content" style="display:none;"></div> - <pre id="test"></pre> - </body> - <label id="test-result"/> -</window>
deleted file mode 100644 --- a/toolkit/components/osfile/tests/mochi/test_osfile_back.xhtml +++ /dev/null @@ -1,44 +0,0 @@ -<?xml version="1.0"?> -<!-- - Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ ---> -<window title="Testing OS.File on a chrome worker thread" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - onload="test();"> - - <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> - <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/> - <script type="application/javascript" - src="chrome://mochikit/content/tests/SimpleTest/WorkerHandler.js"/> - <script type="application/javascript"> - <![CDATA[ - -let worker; - -function test() { - ok(true, "test_osfile.xul: Starting test"); - if (navigator.platform.includes("Win")) { - ok(true, "test_osfile.xul: Using Windows test suite"); - worker = new ChromeWorker("worker_test_osfile_win.js"); - } else { - ok(true, "test_osfile.xul: Using Unix test suite"); - worker = new ChromeWorker("worker_test_osfile_unix.js"); - } - SimpleTest.waitForExplicitFinish(); - ok(true, "test_osfile.xul: Chrome worker created"); - dump("MAIN: go\n"); - listenForTests(worker); - worker.postMessage(0); - ok(true, "test_osfile.xul: Test in progress"); -}; -]]> - </script> - - <body xmlns="http://www.w3.org/1999/xhtml"> - <p id="display"></p> - <div id="content" style="display:none;"></div> - <pre id="test"></pre> - </body> - <label id="test-result"/> -</window>
deleted file mode 100644 --- a/toolkit/components/osfile/tests/mochi/test_osfile_comms.xhtml +++ /dev/null @@ -1,88 +0,0 @@ -<?xml version="1.0"?> -<!-- - Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ ---> -<window title="Testing OS.File on a chrome worker thread" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - onload="test();"> - - <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> - <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/> - <script type="application/javascript"> - <![CDATA[ - -"use strict"; - -let worker; - -SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal", - true]]}); -let test = function test() { - SimpleTest.info("test_osfile_comms.xhtml: Starting test"); - // These are used in the worker. - // eslint-disable-next-line no-unused-vars - let { ctypes } = ChromeUtils.import("resource://gre/modules/ctypes.jsm"); - // eslint-disable-next-line no-unused-vars - let { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - worker = new ChromeWorker("worker_test_osfile_comms.js"); - SimpleTest.waitForExplicitFinish(); - try { - worker.onerror = function onerror(error) { - SimpleTest.ok(false, "received error "+error); - } - worker.onmessage = function onmessage(msg) { - Cu.forceShrinkingGC(); - switch (msg.data.kind) { - case "is": - SimpleTest.ok(msg.data.outcome, msg.data.description + - " ("+ msg.data.a + " ==? " + msg.data.b + ")" ); - return; - case "isnot": - SimpleTest.ok(msg.data.outcome, msg.data.description + - " ("+ msg.data.a + " !=? " + msg.data.b + ")" ); - return; - case "ok": - SimpleTest.ok(msg.data.condition, msg.data.description); - return; - case "info": - SimpleTest.info(msg.data.description); - return; - case "finish": - SimpleTest.finish(); - return; - case "value": - SimpleTest.ok(true, "test_osfile_comms.xhtml: Received value " + JSON.stringify(msg.data.value)); - // eslint-disable-next-line no-eval - let type = eval(msg.data.typename); - // eslint-disable-next-line no-eval - let check = eval(msg.data.check); - let value = msg.data.value; - let deserialized = type.fromMsg(value); - check(deserialized, "Main thread test: "); - return; - default: - SimpleTest.ok(false, "test_osfile_comms.xhtml: wrong message "+JSON.stringify(msg.data)); - } - }; - worker.postMessage(0) - ok(true, "Worker launched"); - } catch(x) { - // As we have set |waitForExplicitFinish|, we add this fallback - // in case of uncaught error, to ensure that the test does not - // just freeze. - ok(false, "Caught exception " + x + " at " + x.stack); - SimpleTest.finish(); - } -}; - -]]> - </script> - - <body xmlns="http://www.w3.org/1999/xhtml"> - <p id="display"></p> - <div id="content" style="display:none;"></div> - <pre id="test"></pre> - </body> - <label id="test-result"/> -</window>
deleted file mode 100644 --- a/toolkit/components/osfile/tests/mochi/test_osfile_front.xhtml +++ /dev/null @@ -1,42 +0,0 @@ -<?xml version="1.0"?> -<!-- - Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ ---> -<window title="Testing OS.File on a chrome worker thread" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - onload="test();"> - - <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> - <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/> - <script type="application/javascript" - src="chrome://mochikit/content/tests/SimpleTest/WorkerHandler.js"/> - <script type="application/javascript"> - <![CDATA[ - -let worker; -let main = this; - -function test() { - info("test_osfile_front.xhtml: Starting test"); - - // Test the OS.File worker - - worker = new ChromeWorker("worker_test_osfile_front.js"); - SimpleTest.waitForExplicitFinish(); - info("test_osfile_front.xhtml: Chrome worker created"); - dump("MAIN: go\n"); - listenForTests(worker); - worker.postMessage(0); - ok(true, "test_osfile_front.xhtml: Test in progress"); -}; -]]> - </script> - - <body xmlns="http://www.w3.org/1999/xhtml"> - <p id="display"></p> - <div id="content" style="display:none;"></div> - <pre id="test"></pre> - </body> - <label id="test-result"/> -</window>
deleted file mode 100644 --- a/toolkit/components/osfile/tests/mochi/worker_test_osfile_comms.js +++ /dev/null @@ -1,205 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/* eslint-env mozilla/chrome-worker, node */ - -"use strict"; - -/* import-globals-from /testing/mochitest/tests/SimpleTest/WorkerSimpleTest.js */ -importScripts("chrome://mochikit/content/tests/SimpleTest/WorkerSimpleTest.js"); - -// The set of samples for communications test. Declare as a global -// variable to prevent this from being garbage-collected too early. -var samples; - -self.onmessage = function(msg) { - info("Initializing"); - self.onmessage = function on_unexpected_message(msg) { - throw new Error("Unexpected message " + JSON.stringify(msg.data)); - }; - /* import-globals-from /toolkit/components/osfile/osfile.jsm */ - importScripts("resource://gre/modules/osfile.jsm"); - info("Initialization complete"); - - samples = [ - { - typename: "OS.Shared.Type.char.in_ptr", - valuedescr: "String", - value: "This is a test", - type: OS.Shared.Type.char.in_ptr, - check: function check_string(candidate, prefix) { - is(candidate, "This is a test", prefix); - }, - }, - { - typename: "OS.Shared.Type.char.in_ptr", - valuedescr: "Typed array", - value: (function() { - let view = new Uint8Array(15); - for (let i = 0; i < 15; ++i) { - view[i] = i; - } - return view; - })(), - type: OS.Shared.Type.char.in_ptr, - check: function check_ArrayBuffer(candidate, prefix) { - for (let i = 0; i < 15; ++i) { - is( - candidate[i], - i % 256, - prefix + - "Checking that the contents of the ArrayBuffer were preserved" - ); - } - }, - }, - { - typename: "OS.Shared.Type.char.in_ptr", - valuedescr: "Pointer", - value: new OS.Shared.Type.char.in_ptr.implementation(1), - type: OS.Shared.Type.char.in_ptr, - check: function check_ptr(candidate, prefix) { - let address = ctypes.cast(candidate, ctypes.uintptr_t).value.toString(); - is( - address, - "1", - prefix + "Checking that the pointer address was preserved" - ); - }, - }, - { - typename: "OS.Shared.Type.char.in_ptr", - valuedescr: "C array", - value: (function() { - let buf = new (ctypes.ArrayType(ctypes.uint8_t, 15))(); - for (let i = 0; i < 15; ++i) { - buf[i] = i % 256; - } - return buf; - })(), - type: OS.Shared.Type.char.in_ptr, - check: function check_array(candidate, prefix) { - let cast = ctypes.cast(candidate, ctypes.uint8_t.ptr); - for (let i = 0; i < 15; ++i) { - is( - cast.contents, - i % 256, - prefix + - "Checking that the contents of the C array were preserved, index " + - i - ); - cast = cast.increment(); - } - }, - }, - { - typename: "OS.File.Error", - valuedescr: "OS Error", - type: OS.File.Error, - value: new OS.File.Error("foo", 1), - check: function check_error(candidate, prefix) { - ok( - candidate instanceof OS.File.Error, - prefix + "Error is an OS.File.Error" - ); - ok( - candidate.unixErrno == 1 || candidate.winLastError == 1, - prefix + "Error code is correct" - ); - try { - let string = candidate.toString(); - info(prefix + ".toString() works " + string); - } catch (x) { - ok(false, prefix + ".toString() fails " + x); - } - }, - }, - ]; - samples.forEach(function test(sample) { - let type = sample.type; - let value = sample.value; - let check = sample.check; - info( - "Testing handling of type " + - sample.typename + - " communicating " + - sample.valuedescr - ); - - // 1. Test serialization - let serialized; - let exn; - try { - serialized = type.toMsg(value); - } catch (ex) { - exn = ex; - } - is( - exn, - null, - "Can I serialize the following value? " + - value + - " aka " + - JSON.stringify(value) - ); - if (exn) { - return; - } - - if ("data" in serialized) { - // Unwrap from `Meta` - serialized = serialized.data; - } - - // 2. Test deserialization - let deserialized; - try { - deserialized = type.fromMsg(serialized); - } catch (ex) { - exn = ex; - } - is( - exn, - null, - "Can I deserialize the following message? " + - serialized + - " aka " + - JSON.stringify(serialized) - ); - if (exn) { - return; - } - - // 3. Local test deserialized value - info( - "Running test on deserialized value " + - deserialized + - " aka " + - JSON.stringify(deserialized) - ); - check(deserialized, "Local test: "); - - // 4. Test sending serialized - info("Attempting to send message"); - try { - self.postMessage({ - kind: "value", - typename: sample.typename, - value: serialized, - check: check.toSource(), - }); - } catch (ex) { - exn = ex; - } - is( - exn, - null, - "Can I send the following message? " + - serialized + - " aka " + - JSON.stringify(serialized) - ); - }); - - finish(); -};
deleted file mode 100644 --- a/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js +++ /dev/null @@ -1,696 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/* eslint-env mozilla/chrome-worker, node */ - -/* import-globals-from /testing/mochitest/tests/SimpleTest/WorkerSimpleTest.js */ -importScripts("chrome://mochikit/content/tests/SimpleTest/WorkerSimpleTest.js"); -/* import-globals-from /toolkit/components/workerloader/require.js */ -importScripts("resource://gre/modules/workers/require.js"); - -var SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm"); -SharedAll.Config.DEBUG = true; - -function should_throw(f) { - try { - f(); - } catch (x) { - return x; - } - return null; -} - -self.onmessage = function onmessage_start(msg) { - self.onmessage = function onmessage_ignored(msg) { - log("ignored message " + JSON.stringify(msg.data)); - }; - try { - test_init(); - test_open_existing_file(); - test_open_non_existing_file(); - test_flush_open_file(); - test_copy_existing_file(); - test_position(); - test_move_file(); - test_iter_dir(); - test_info(); - test_path(); - test_exists_file(); - test_remove_file(); - } catch (x) { - log("Catching error: " + x); - log("Stack: " + x.stack); - log("Source: " + x.toSource()); - ok(false, x.toString() + "\n" + x.stack); - } - finish(); -}; - -function test_init() { - info("Starting test_init"); - /* import-globals-from /toolkit/components/osfile/osfile.jsm */ - importScripts("resource://gre/modules/osfile.jsm"); -} - -/** - * Test that we can open an existing file. - */ -function test_open_existing_file() { - info("Starting test_open_existing"); - let file = OS.File.open( - "chrome/toolkit/components/osfile/tests/mochi/worker_test_osfile_unix.js" - ); - file.close(); -} - -/** - * Test that opening a file that does not exist fails with the right error. - */ -function test_open_non_existing_file() { - info("Starting test_open_non_existing"); - let exn; - try { - OS.File.open("/I do not exist"); - } catch (x) { - exn = x; - info("test_open_non_existing_file: Exception detail " + exn); - } - ok(!!exn, "test_open_non_existing_file: Exception was raised "); - ok( - exn instanceof OS.File.Error, - "test_open_non_existing_file: Exception was a OS.File.Error" - ); - ok( - exn.becauseNoSuchFile, - "test_open_non_existing_file: Exception confirms that the file does not exist" - ); -} - -/** - * Test that to ensure that |foo.flush()| does not - * cause an error, where |foo| is an open file. - */ -function test_flush_open_file() { - info("Starting test_flush_open_file"); - let tmp = "test_flush.tmp"; - let file = OS.File.open(tmp, { create: true, write: true }); - file.flush(); - file.close(); - OS.File.remove(tmp); -} - -/** - * Utility function for comparing two files (or a prefix of two files). - * - * This function returns nothing but fails of both files (or prefixes) - * are not identical. - * - * @param {string} test The name of the test (used for logging). - * @param {string} sourcePath The name of the first file. - * @param {string} destPath The name of the second file. - * @param {number=} prefix If specified, only compare the |prefix| - * first bytes of |sourcePath| and |destPath|. - */ -function compare_files(test, sourcePath, destPath, prefix) { - info(test + ": Comparing " + sourcePath + " and " + destPath); - let source = OS.File.open(sourcePath); - let dest = OS.File.open(destPath); - info("Files are open"); - let sourceResult, destResult; - try { - if (prefix != undefined) { - sourceResult = source.read(prefix); - destResult = dest.read(prefix); - } else { - sourceResult = source.read(); - destResult = dest.read(); - } - is( - sourceResult.length, - destResult.length, - test + ": Both files have the same size" - ); - for (let i = 0; i < sourceResult.length; ++i) { - if (sourceResult[i] != destResult[i]) { - is(sourceResult[i] != destResult[i], test + ": Comparing char " + i); - break; - } - } - } finally { - source.close(); - dest.close(); - } - info(test + ": Comparison complete"); -} - -/** - * Test that copying a file using |copy| works. - */ -function test_copy_existing_file() { - let src_file_name = OS.Path.join( - "chrome", - "toolkit", - "components", - "osfile", - "tests", - "mochi", - "worker_test_osfile_front.js" - ); - let tmp_file_name = "test_osfile_front.tmp"; - info("Starting test_copy_existing"); - OS.File.copy(src_file_name, tmp_file_name); - - info("test_copy_existing: Copy complete"); - compare_files("test_copy_existing", src_file_name, tmp_file_name); - - // Create a bogus file with arbitrary content, then attempt to overwrite - // it with |copy|. - let dest = OS.File.open(tmp_file_name, { trunc: true }); - let buf = new Uint8Array(50); - dest.write(buf); - dest.close(); - - OS.File.copy(src_file_name, tmp_file_name); - - compare_files("test_copy_existing 2", src_file_name, tmp_file_name); - - // Attempt to overwrite with noOverwrite - let exn; - try { - OS.File.copy(src_file_name, tmp_file_name, { noOverwrite: true }); - } catch (x) { - exn = x; - } - ok( - !!exn, - "test_copy_existing: noOverwrite prevents overwriting existing files" - ); - - info("test_copy_existing: Cleaning up"); - OS.File.remove(tmp_file_name); -} - -/** - * Test that moving a file works. - */ -function test_move_file() { - info("test_move_file: Starting"); - // 1. Copy file into a temporary file - let src_file_name = OS.Path.join( - "chrome", - "toolkit", - "components", - "osfile", - "tests", - "mochi", - "worker_test_osfile_front.js" - ); - let tmp_file_name = "test_osfile_front.tmp"; - let tmp2_file_name = "test_osfile_front.tmp2"; - OS.File.copy(src_file_name, tmp_file_name); - - info("test_move_file: Copy complete"); - - // 2. Move - OS.File.move(tmp_file_name, tmp2_file_name); - - info("test_move_file: Move complete"); - - // 3. Check that destination exists - compare_files("test_move_file", src_file_name, tmp2_file_name); - - // 4. Check that original file does not exist anymore - let exn; - try { - OS.File.open(tmp_file_name); - } catch (x) { - exn = x; - } - ok(!!exn, "test_move_file: Original file has been removed"); - - info("test_move_file: Cleaning up"); - OS.File.remove(tmp2_file_name); -} - -function test_iter_dir() { - info("test_iter_dir: Starting"); - - // Create a file, to be sure that it exists - let tmp_file_name = "test_osfile_front.tmp"; - let tmp_file = OS.File.open(tmp_file_name, { write: true, trunc: true }); - tmp_file.close(); - - let parent = OS.File.getCurrentDirectory(); - info("test_iter_dir: directory " + parent); - let iterator = new OS.File.DirectoryIterator(parent); - info("test_iter_dir: iterator created"); - let encountered_tmp_file = false; - for (let entry of iterator) { - // Checking that |name| can be decoded properly - info("test_iter_dir: encountering entry " + entry.name); - - if (entry.name == tmp_file_name) { - encountered_tmp_file = true; - isnot( - entry.isDir, - "test_iter_dir: The temporary file is not a directory" - ); - isnot(entry.isSymLink, "test_iter_dir: The temporary file is not a link"); - } - - let file; - let success = true; - try { - file = OS.File.open(entry.path); - } catch (x) { - if (x.becauseNoSuchFile) { - success = false; - } - } - if (file) { - file.close(); - } - ok(success, "test_iter_dir: Entry " + entry.path + " exists"); - - if (OS.Win) { - // We assume that the files are at least as recent as 2009. - // Since this test was written in 2011 and some of our packaging - // sets dates arbitrarily to 2010, this should be safe. - let year = new Date().getFullYear(); - - let lastWrite = entry.winLastWriteDate; - ok( - lastWrite, - "test_iter_dir: Windows lastWrite date exists: " + lastWrite - ); - ok( - lastWrite.getFullYear() >= 2009 && lastWrite.getFullYear() <= year, - "test_iter_dir: consistent lastWrite date" - ); - - let lastAccess = entry.winLastAccessDate; - ok( - lastAccess, - "test_iter_dir: Windows lastAccess date exists: " + lastAccess - ); - ok( - lastAccess.getFullYear() >= 2009 && lastAccess.getFullYear() <= year, - "test_iter_dir: consistent lastAccess date" - ); - } - } - ok(encountered_tmp_file, "test_iter_dir: We have found the temporary file"); - - info("test_iter_dir: Cleaning up"); - iterator.close(); - - // Testing nextBatch() - iterator = new OS.File.DirectoryIterator(parent); - let allentries = []; - for (let x of iterator) { - allentries.push(x); - } - iterator.close(); - - ok( - allentries.length >= 14, - "test_iter_dir: Meta-check: the test directory should contain at least 14 items" - ); - - iterator = new OS.File.DirectoryIterator(parent); - let firstten = iterator.nextBatch(10); - is(firstten.length, 10, "test_iter_dir: nextBatch(10) returns 10 items"); - for (let i = 0; i < firstten.length; ++i) { - is( - allentries[i].path, - firstten[i].path, - "test_iter_dir: Checking that batch returns the correct entries" - ); - } - let nextthree = iterator.nextBatch(3); - is(nextthree.length, 3, "test_iter_dir: nextBatch(3) returns 3 items"); - for (let i = 0; i < nextthree.length; ++i) { - is( - allentries[i + firstten.length].path, - nextthree[i].path, - "test_iter_dir: Checking that batch 2 returns the correct entries" - ); - } - let everythingelse = iterator.nextBatch(); - ok( - everythingelse.length >= 1, - "test_iter_dir: nextBatch() returns at least one item" - ); - for (let i = 0; i < everythingelse.length; ++i) { - is( - allentries[i + firstten.length + nextthree.length].path, - everythingelse[i].path, - "test_iter_dir: Checking that batch 3 returns the correct entries" - ); - } - is( - iterator.nextBatch().length, - 0, - "test_iter_dir: Once there is nothing left, nextBatch returns an empty array" - ); - iterator.close(); - - iterator = new OS.File.DirectoryIterator(parent); - iterator.close(); - is( - iterator.nextBatch().length, - 0, - "test_iter_dir: nextBatch on closed iterator returns an empty array" - ); - - iterator = new OS.File.DirectoryIterator(parent); - let allentries2 = iterator.nextBatch(); - is( - allentries.length, - allentries2.length, - "test_iter_dir: Checking that getBatch(null) returns the right number of entries" - ); - for (let i = 0; i < allentries.length; ++i) { - is( - allentries[i].path, - allentries2[i].path, - "test_iter_dir: Checking that getBatch(null) returns everything in the right order" - ); - } - iterator.close(); - - // Test forEach - iterator = new OS.File.DirectoryIterator(parent); - let index = 0; - iterator.forEach(function cb(entry, aIndex, aIterator) { - is(index, aIndex, "test_iter_dir: Checking that forEach index is correct"); - ok( - iterator == aIterator, - "test_iter_dir: Checking that right iterator is passed" - ); - if (index < 10) { - is( - allentries[index].path, - entry.path, - "test_iter_dir: Checking that forEach entry is correct" - ); - } else if (index == 10) { - iterator.close(); - } else { - ok(false, "test_iter_dir: Checking that forEach can be stopped early"); - } - ++index; - }); - iterator.close(); - - // test for prototype |OS.File.DirectoryIterator.unixAsFile| - if ("unixAsFile" in OS.File.DirectoryIterator.prototype) { - info("testing property unixAsFile"); - let path = OS.Path.join( - "chrome", - "toolkit", - "components", - "osfile", - "tests", - "mochi" - ); - iterator = new OS.File.DirectoryIterator(path); - - let dir_file = iterator.unixAsFile(); // return |File| - let stat0 = dir_file.stat(); - let stat1 = OS.File.stat(path); - - let unix_info_to_string = function unix_info_to_string(info) { - return ( - "| " + - info.unixMode + - " | " + - info.unixOwner + - " | " + - info.unixGroup + - " | " + - info.lastModificationDate + - " | " + - info.lastAccessDate + - " | " + - info.size + - " |" - ); - }; - - let s0_string = unix_info_to_string(stat0); - let s1_string = unix_info_to_string(stat1); - - ok(stat0.isDir, "unixAsFile returned a directory"); - is(s0_string, s1_string, "unixAsFile returned the correct file"); - dir_file.close(); - iterator.close(); - } - info("test_iter_dir: Complete"); -} - -function test_position() { - info("test_position: Starting"); - - ok("POS_START" in OS.File, "test_position: POS_START exists"); - ok("POS_CURRENT" in OS.File, "test_position: POS_CURRENT exists"); - ok("POS_END" in OS.File, "test_position: POS_END exists"); - - let ARBITRARY_POSITION = 321; - let src_file_name = OS.Path.join( - "chrome", - "toolkit", - "components", - "osfile", - "tests", - "mochi", - "worker_test_osfile_front.js" - ); - - let file = OS.File.open(src_file_name); - is(file.getPosition(), 0, "test_position: Initial position is 0"); - - let size = 0 + file.stat().size; // Hack: We can remove this 0 + once 776259 has landed - - file.setPosition(ARBITRARY_POSITION, OS.File.POS_START); - is( - file.getPosition(), - ARBITRARY_POSITION, - "test_position: Setting position from start" - ); - - file.setPosition(0, OS.File.POS_START); - is( - file.getPosition(), - 0, - "test_position: Setting position from start back to 0" - ); - - file.setPosition(ARBITRARY_POSITION); - is( - file.getPosition(), - ARBITRARY_POSITION, - "test_position: Setting position without argument" - ); - - file.setPosition(-ARBITRARY_POSITION, OS.File.POS_END); - is( - file.getPosition(), - size - ARBITRARY_POSITION, - "test_position: Setting position from end" - ); - - file.setPosition(ARBITRARY_POSITION, OS.File.POS_CURRENT); - is(file.getPosition(), size, "test_position: Setting position from current"); - - file.close(); - info("test_position: Complete"); -} - -function test_info() { - info("test_info: Starting"); - - let filename = "test_info.tmp"; - let size = 261; // An arbitrary file length - let start = new Date(); - - // Cleanup any leftover from previous tests - try { - OS.File.remove(filename); - info("test_info: Cleaned up previous garbage"); - } catch (x) { - if (!x.becauseNoSuchFile) { - throw x; - } - info("test_info: No previous garbage"); - } - - let file = OS.File.open(filename, { trunc: true }); - let buf = new ArrayBuffer(size); - file._write(buf, size); - file.close(); - - // Test OS.File.stat on new file - let stat = OS.File.stat(filename); - ok(!!stat, "test_info: info acquired"); - ok(!stat.isDir, "test_info: file is not a directory"); - is(stat.isSymLink, false, "test_info: file is not a link"); - is(stat.size.toString(), size, "test_info: correct size"); - - let stop = new Date(); - - // We round down/up by 1s as file system precision is lower than - // Date precision (no clear specifications about that, but it seems - // that this can be a little over 1 second under ext3 and 2 seconds - // under FAT). - let SLOPPY_FILE_SYSTEM_ADJUSTMENT = 3000; - let startMs = start.getTime() - SLOPPY_FILE_SYSTEM_ADJUSTMENT; - let stopMs = stop.getTime() + SLOPPY_FILE_SYSTEM_ADJUSTMENT; - info("Testing stat with bounds [ " + startMs + ", " + stopMs + " ]"); - - let change = stat.lastModificationDate; - info("Testing lastModificationDate: " + change); - ok( - change.getTime() >= startMs && change.getTime() <= stopMs, - "test_info: lastModificationDate is consistent" - ); - - // Test OS.File.prototype.stat on new file - file = OS.File.open(filename); - try { - stat = file.stat(); - } finally { - file.close(); - } - - ok(!!stat, "test_info: info acquired 2"); - ok(!stat.isDir, "test_info: file is not a directory 2"); - ok(!stat.isSymLink, "test_info: file is not a link 2"); - is(stat.size.toString(), size, "test_info: correct size 2"); - - stop = new Date(); - - // Round up/down as above - startMs = start.getTime() - SLOPPY_FILE_SYSTEM_ADJUSTMENT; - stopMs = stop.getTime() + SLOPPY_FILE_SYSTEM_ADJUSTMENT; - info("Testing stat 2 with bounds [ " + startMs + ", " + stopMs + " ]"); - - let access = stat.lastAccessDate; - info("Testing lastAccessDate: " + access); - ok( - access.getTime() >= startMs && access.getTime() <= stopMs, - "test_info: lastAccessDate is consistent" - ); - - change = stat.lastModificationDate; - info("Testing lastModificationDate 2: " + change); - ok( - change.getTime() >= startMs && change.getTime() <= stopMs, - "test_info: lastModificationDate 2 is consistent" - ); - - // Test OS.File.stat on directory - stat = OS.File.stat(OS.File.getCurrentDirectory()); - ok(!!stat, "test_info: info on directory acquired"); - ok(stat.isDir, "test_info: directory is a directory"); - - info("test_info: Complete"); -} - -// Note that most of the features of path are tested in -// worker_test_osfile_{unix, win}.js -function test_path() { - info("test_path: starting"); - let abcd = OS.Path.join("a", "b", "c", "d"); - is(OS.Path.basename(abcd), "d", "basename of a/b/c/d"); - - let abc = OS.Path.join("a", "b", "c"); - is(OS.Path.dirname(abcd), abc, "dirname of a/b/c/d"); - - let abdotsc = OS.Path.join("a", "b", "..", "c"); - is(OS.Path.normalize(abdotsc), OS.Path.join("a", "c"), "normalize a/b/../c"); - - let adotsdotsdots = OS.Path.join("a", "..", "..", ".."); - is( - OS.Path.normalize(adotsdotsdots), - OS.Path.join("..", ".."), - "normalize a/../../.." - ); - - info("test_path: Complete"); -} - -/** - * Test the file |exists| method. - */ -function test_exists_file() { - let file_name = OS.Path.join( - "chrome", - "toolkit", - "components", - "osfile", - "tests", - "mochi", - "test_osfile_front.xhtml" - ); - info("test_exists_file: starting"); - ok( - OS.File.exists(file_name), - "test_exists_file: file exists (OS.File.exists)" - ); - ok( - !OS.File.exists(file_name + ".tmp"), - "test_exists_file: file does not exists (OS.File.exists)" - ); - - let dir_name = OS.Path.join( - "chrome", - "toolkit", - "components", - "osfile", - "tests", - "mochi" - ); - ok(OS.File.exists(dir_name), "test_exists_file: directory exists"); - ok( - !OS.File.exists(dir_name) + ".tmp", - "test_exists_file: directory does not exist" - ); - - info("test_exists_file: complete"); -} - -/** - * Test the file |remove| method. - */ -function test_remove_file() { - let absent_file_name = "test_osfile_front_absent.tmp"; - - // Check that removing absent files is handled correctly - let exn = should_throw(function() { - OS.File.remove(absent_file_name, { ignoreAbsent: false }); - }); - ok(!!exn, "test_remove_file: throws if there is no such file"); - - exn = should_throw(function() { - OS.File.remove(absent_file_name, { ignoreAbsent: true }); - OS.File.remove(absent_file_name); - }); - ok(!exn, "test_remove_file: ignoreAbsent works"); - - if (OS.Win) { - let file_name = "test_osfile_front_file_to_remove.tmp"; - let file = OS.File.open(file_name, { write: true }); - file.close(); - ok(OS.File.exists(file_name), "test_remove_file: test file exists"); - OS.Win.File.SetFileAttributes( - file_name, - OS.Constants.Win.FILE_ATTRIBUTE_READONLY - ); - OS.File.remove(file_name); - ok( - !OS.File.exists(file_name), - "test_remove_file: test file has been removed" - ); - } -}
deleted file mode 100644 --- a/toolkit/components/osfile/tests/mochi/worker_test_osfile_unix.js +++ /dev/null @@ -1,257 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/* eslint-env mozilla/chrome-worker, node */ - -/* import-globals-from /testing/mochitest/tests/SimpleTest/WorkerSimpleTest.js */ -importScripts("chrome://mochikit/content/tests/SimpleTest/WorkerSimpleTest.js"); - -self.onmessage = function(msg) { - log("received message " + JSON.stringify(msg.data)); - self.onmessage = function(msg) { - log("ignored message " + JSON.stringify(msg.data)); - }; - test_init(); - test_getcwd(); - test_open_close(); - test_create_file(); - test_access(); - test_read_write(); - test_passing_undefined(); - finish(); -}; - -function test_init() { - info("Starting test_init"); - /* import-globals-from /toolkit/components/osfile/osfile.jsm */ - importScripts("resource://gre/modules/osfile.jsm"); -} - -function test_open_close() { - info("Starting test_open_close"); - is(typeof OS.Unix.File.open, "function", "OS.Unix.File.open is a function"); - let file = OS.Unix.File.open( - "chrome/toolkit/components/osfile/tests/mochi/worker_test_osfile_unix.js", - OS.Constants.libc.O_RDONLY - ); - isnot(file, -1, "test_open_close: opening succeeded"); - info("Close: " + OS.Unix.File.close.toSource()); - let result = OS.Unix.File.close(file); - is(result, 0, "test_open_close: close succeeded"); - - file = OS.Unix.File.open("/i do not exist", OS.Constants.libc.O_RDONLY); - is(file, -1, "test_open_close: opening of non-existing file failed"); - is( - ctypes.errno, - OS.Constants.libc.ENOENT, - "test_open_close: error is ENOENT" - ); -} - -function test_create_file() { - info("Starting test_create_file"); - let file = OS.Unix.File.open( - "test.tmp", - OS.Constants.libc.O_RDWR | - OS.Constants.libc.O_CREAT | - OS.Constants.libc.O_TRUNC, - ctypes.int(OS.Constants.libc.S_IRWXU) - ); - isnot(file, -1, "test_create_file: file created"); - OS.Unix.File.close(file); -} - -function test_access() { - info("Starting test_access"); - let file = OS.Unix.File.open( - "test1.tmp", - OS.Constants.libc.O_RDWR | - OS.Constants.libc.O_CREAT | - OS.Constants.libc.O_TRUNC, - ctypes.int(OS.Constants.libc.S_IRWXU) - ); - let result = OS.Unix.File.access( - "test1.tmp", - OS.Constants.libc.R_OK | - OS.Constants.libc.W_OK | - OS.Constants.libc.X_OK | - OS.Constants.libc.F_OK - ); - is(result, 0, "first call to access() succeeded"); - OS.Unix.File.close(file); - - file = OS.Unix.File.open( - "test1.tmp", - OS.Constants.libc.O_WRONLY | - OS.Constants.libc.O_CREAT | - OS.Constants.libc.O_TRUNC, - ctypes.int(OS.Constants.libc.S_IWUSR) - ); - - info("test_access: preparing second call to access()"); - result = OS.Unix.File.access( - "test2.tmp", - OS.Constants.libc.R_OK | - OS.Constants.libc.W_OK | - OS.Constants.libc.X_OK | - OS.Constants.libc.F_OK - ); - is(result, -1, "test_access: second call to access() failed as expected"); - is(ctypes.errno, OS.Constants.libc.ENOENT, "This is the correct error"); - OS.Unix.File.close(file); -} - -function test_getcwd() { - let array = new (ctypes.ArrayType(ctypes.char, 32768))(); - let path = OS.Unix.File.getcwd(array, array.length); - if (ctypes.char.ptr(path).isNull()) { - ok(false, "test_get_cwd: getcwd returned null, errno: " + ctypes.errno); - } - let path2; - if (OS.Unix.File.get_current_dir_name) { - path2 = OS.Unix.File.get_current_dir_name(); - } else { - path2 = OS.Unix.File.getwd_auto(null); - } - if (ctypes.char.ptr(path2).isNull()) { - ok( - false, - "test_get_cwd: getwd_auto/get_current_dir_name returned null, errno: " + - ctypes.errno - ); - } - is( - path.readString(), - path2.readString(), - "test_get_cwd: getcwd and getwd return the same path" - ); -} - -function test_read_write() { - let output_name = "osfile_copy.tmp"; - // Copy file - let input = OS.Unix.File.open( - "chrome/toolkit/components/osfile/tests/mochi/worker_test_osfile_unix.js", - OS.Constants.libc.O_RDONLY - ); - isnot(input, -1, "test_read_write: input file opened"); - let output = OS.Unix.File.open( - "osfile_copy.tmp", - OS.Constants.libc.O_RDWR | - OS.Constants.libc.O_CREAT | - OS.Constants.libc.O_TRUNC, - ctypes.int(OS.Constants.libc.S_IRWXU) - ); - isnot(output, -1, "test_read_write: output file opened"); - - let array = new (ctypes.ArrayType(ctypes.char, 4096))(); - let bytes = -1; - let total = 0; - while (true) { - bytes = OS.Unix.File.read(input, array, 4096); - ok(bytes != undefined, "test_read_write: bytes is defined"); - isnot(bytes, -1, "test_read_write: no read error"); - let write_from = 0; - if (bytes == 0) { - break; - } - while (bytes > 0) { - array.addressOfElement(write_from); - // Note: |write| launches an exception in case of error - let written = OS.Unix.File.write(output, array, bytes); - isnot(written, -1, "test_read_write: no write error"); - write_from += written; - bytes -= written; - } - total += write_from; - } - info("test_read_write: copy complete " + total); - - // Compare files - let result; - info("SEEK_SET: " + OS.Constants.libc.SEEK_SET); - info("Input: " + input + "(" + input.toSource() + ")"); - info("Output: " + output + "(" + output.toSource() + ")"); - result = OS.Unix.File.lseek(input, 0, OS.Constants.libc.SEEK_SET); - info("Result of lseek: " + result); - isnot(result, -1, "test_read_write: input seek succeeded " + ctypes.errno); - result = OS.Unix.File.lseek(output, 0, OS.Constants.libc.SEEK_SET); - isnot(result, -1, "test_read_write: output seek succeeded " + ctypes.errno); - - let array2 = new (ctypes.ArrayType(ctypes.char, 4096))(); - let bytes2 = -1; - let pos = 0; - while (true) { - bytes = OS.Unix.File.read(input, array, 4096); - isnot(bytes, -1, "test_read_write: input read succeeded"); - bytes2 = OS.Unix.File.read(output, array2, 4096); - isnot(bytes, -1, "test_read_write: output read succeeded"); - is( - bytes > 0, - bytes2 > 0, - "Both files contain data or neither does " + bytes + ", " + bytes2 - ); - if (bytes == 0) { - break; - } - if (bytes != bytes2) { - // This would be surprising, but theoretically possible with a - // remote file system, I believe. - bytes = Math.min(bytes, bytes2); - pos += bytes; - result = OS.Unix.File.lseek(input, pos, OS.Constants.libc.SEEK_SET); - isnot(result, -1, "test_read_write: input seek succeeded"); - result = OS.Unix.File.lseek(output, pos, OS.Constants.libc.SEEK_SET); - isnot(result, -1, "test_read_write: output seek succeeded"); - } else { - pos += bytes; - } - for (let i = 0; i < bytes; ++i) { - if (array[i] != array2[i]) { - ok( - false, - "Files do not match at position " + - i + - " (" + - array[i] + - "/" + - array2[i] + - ")" - ); - } - } - } - info("test_read_write test complete"); - result = OS.Unix.File.close(input); - isnot(result, -1, "test_read_write: input close succeeded"); - result = OS.Unix.File.close(output); - isnot(result, -1, "test_read_write: output close succeeded"); - result = OS.Unix.File.unlink(output_name); - isnot(result, -1, "test_read_write: input remove succeeded"); - info("test_read_write cleanup complete"); -} - -function test_passing_undefined() { - info( - "Testing that an exception gets thrown when an FFI function is passed undefined" - ); - let exceptionRaised = false; - - try { - OS.Unix.File.open( - undefined, - OS.Constants.libc.O_RDWR | - OS.Constants.libc.O_CREAT | - OS.Constants.libc.O_TRUNC, - ctypes.int(OS.Constants.libc.S_IRWXU) - ); - } catch (e) { - if (e instanceof TypeError && e.message.indexOf("open") > -1) { - exceptionRaised = true; - } else { - throw e; - } - } - - ok(exceptionRaised, "test_passing_undefined: exception gets thrown"); -}
deleted file mode 100644 --- a/toolkit/components/osfile/tests/mochi/worker_test_osfile_win.js +++ /dev/null @@ -1,310 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/* eslint-env mozilla/chrome-worker, node */ - -/* import-globals-from /testing/mochitest/tests/SimpleTest/WorkerSimpleTest.js */ -importScripts("chrome://mochikit/content/tests/SimpleTest/WorkerSimpleTest.js"); - -self.onmessage = function(msg) { - self.onmessage = function(msg) { - log("ignored message " + JSON.stringify(msg.data)); - }; - - test_init(); - test_GetCurrentDirectory(); - test_OpenClose(); - test_CreateFile(); - test_ReadWrite(); - test_passing_undefined(); - finish(); -}; - -function test_init() { - info("Starting test_init"); - /* import-globals-from /toolkit/components/osfile/osfile.jsm */ - importScripts("resource://gre/modules/osfile.jsm"); -} - -function test_OpenClose() { - info("Starting test_OpenClose"); - is( - typeof OS.Win.File.CreateFile, - "function", - "OS.Win.File.CreateFile is a function" - ); - is( - OS.Win.File.CloseHandle(OS.Constants.Win.INVALID_HANDLE_VALUE), - true, - "CloseHandle returns true given the invalid handle" - ); - is( - OS.Win.File.FindClose(OS.Constants.Win.INVALID_HANDLE_VALUE), - true, - "FindClose returns true given the invalid handle" - ); - isnot(OS.Constants.Win.GENERIC_READ, undefined, "GENERIC_READ exists"); - isnot(OS.Constants.Win.FILE_SHARE_READ, undefined, "FILE_SHARE_READ exists"); - isnot( - OS.Constants.Win.FILE_ATTRIBUTE_NORMAL, - undefined, - "FILE_ATTRIBUTE_NORMAL exists" - ); - let file = OS.Win.File.CreateFile( - "chrome\\toolkit\\components\\osfile\\tests\\mochi\\worker_test_osfile_win.js", - OS.Constants.Win.GENERIC_READ, - 0, - null, - OS.Constants.Win.OPEN_EXISTING, - 0, - null - ); - info("test_OpenClose: Passed open"); - isnot( - file, - OS.Constants.Win.INVALID_HANDLE_VALUE, - "test_OpenClose: file opened" - ); - let result = OS.Win.File.CloseHandle(file); - isnot(result, 0, "test_OpenClose: close succeeded"); - - file = OS.Win.File.CreateFile( - "\\I do not exist", - OS.Constants.Win.GENERIC_READ, - OS.Constants.Win.FILE_SHARE_READ, - null, - OS.Constants.Win.OPEN_EXISTING, - OS.Constants.Win.FILE_ATTRIBUTE_NORMAL, - null - ); - is( - file, - OS.Constants.Win.INVALID_HANDLE_VALUE, - "test_OpenClose: cannot open non-existing file" - ); - is( - ctypes.winLastError, - OS.Constants.Win.ERROR_FILE_NOT_FOUND, - "test_OpenClose: error is ERROR_FILE_NOT_FOUND" - ); -} - -function test_CreateFile() { - info("Starting test_CreateFile"); - let file = OS.Win.File.CreateFile( - "test.tmp", - OS.Constants.Win.GENERIC_READ | OS.Constants.Win.GENERIC_WRITE, - OS.Constants.Win.FILE_SHARE_READ | OS.Constants.FILE_SHARE_WRITE, - null, - OS.Constants.Win.CREATE_ALWAYS, - OS.Constants.Win.FILE_ATTRIBUTE_NORMAL, - null - ); - isnot( - file, - OS.Constants.Win.INVALID_HANDLE_VALUE, - "test_CreateFile: opening succeeded" - ); - let result = OS.Win.File.CloseHandle(file); - isnot(result, 0, "test_CreateFile: close succeeded"); -} - -function test_GetCurrentDirectory() { - let array = new (ctypes.ArrayType(ctypes.char16_t, 4096))(); - let result = OS.Win.File.GetCurrentDirectory(4096, array); - ok(result < array.length, "test_GetCurrentDirectory: length sufficient"); - ok(result > 0, "test_GetCurrentDirectory: length != 0"); -} - -function test_ReadWrite() { - info("Starting test_ReadWrite"); - let output_name = "osfile_copy.tmp"; - // Copy file - let input = OS.Win.File.CreateFile( - "chrome\\toolkit\\components\\osfile\\tests\\mochi\\worker_test_osfile_win.js", - OS.Constants.Win.GENERIC_READ, - 0, - null, - OS.Constants.Win.OPEN_EXISTING, - 0, - null - ); - isnot( - input, - OS.Constants.Win.INVALID_HANDLE_VALUE, - "test_ReadWrite: input file opened" - ); - let output = OS.Win.File.CreateFile( - "osfile_copy.tmp", - OS.Constants.Win.GENERIC_READ | OS.Constants.Win.GENERIC_WRITE, - 0, - null, - OS.Constants.Win.CREATE_ALWAYS, - OS.Constants.Win.FILE_ATTRIBUTE_NORMAL, - null - ); - isnot( - output, - OS.Constants.Win.INVALID_HANDLE_VALUE, - "test_ReadWrite: output file opened" - ); - let array = new (ctypes.ArrayType(ctypes.char, 4096))(); - let bytes_read = new ctypes.uint32_t(0); - let bytes_read_ptr = bytes_read.address(); - log("We have a pointer for bytes read: " + bytes_read_ptr); - let bytes_written = new ctypes.uint32_t(0); - let bytes_written_ptr = bytes_written.address(); - log("We have a pointer for bytes written: " + bytes_written_ptr); - log("test_ReadWrite: buffer and pointers ready"); - let result; - while (true) { - log("test_ReadWrite: reading"); - result = OS.Win.File.ReadFile(input, array, 4096, bytes_read_ptr, null); - isnot(result, 0, "test_ReadWrite: read success"); - let write_from = 0; - let bytes_left = bytes_read; - log("test_ReadWrite: read chunk complete " + bytes_left.value); - if (bytes_left.value == 0) { - break; - } - while (bytes_left.value > 0) { - log("test_ReadWrite: writing " + bytes_left.value); - array.addressOfElement(write_from); - // Note: |WriteFile| launches an exception in case of error - result = OS.Win.File.WriteFile( - output, - array, - bytes_left, - bytes_written_ptr, - null - ); - isnot(result, 0, "test_ReadWrite: write success"); - write_from += bytes_written; - bytes_left -= bytes_written; - } - } - info("test_ReadWrite: copy complete"); - - // Compare files - result = OS.Win.File.SetFilePointer( - input, - 0, - null, - OS.Constants.Win.FILE_BEGIN - ); - isnot( - result, - OS.Constants.Win.INVALID_SET_FILE_POINTER, - "test_ReadWrite: input reset" - ); - - result = OS.Win.File.SetFilePointer( - output, - 0, - null, - OS.Constants.Win.FILE_BEGIN - ); - isnot( - result, - OS.Constants.Win.INVALID_SET_FILE_POINTER, - "test_ReadWrite: output reset" - ); - - let array2 = new (ctypes.ArrayType(ctypes.char, 4096))(); - let bytes_read2 = new ctypes.uint32_t(0); - let bytes_read2_ptr = bytes_read2.address(); - let pos = 0; - while (true) { - result = OS.Win.File.ReadFile(input, array, 4096, bytes_read_ptr, null); - isnot(result, 0, "test_ReadWrite: input read succeeded"); - - result = OS.Win.File.ReadFile(output, array2, 4096, bytes_read2_ptr, null); - isnot(result, 0, "test_ReadWrite: output read succeeded"); - - is( - bytes_read.value > 0, - bytes_read2.value > 0, - "Both files contain data or neither does " + - bytes_read.value + - ", " + - bytes_read2.value - ); - if (bytes_read.value == 0) { - break; - } - let bytes; - if (bytes_read.value != bytes_read2.value) { - // This would be surprising, but theoretically possible with a - // remote file system, I believe. - bytes = Math.min(bytes_read.value, bytes_read2.value); - pos += bytes; - result = OS.Win.File.SetFilePointer( - input, - pos, - null, - OS.Constants.Win.FILE_BEGIN - ); - isnot(result, 0, "test_ReadWrite: input seek succeeded"); - - result = OS.Win.File.SetFilePointer( - output, - pos, - null, - OS.Constants.Win.FILE_BEGIN - ); - isnot(result, 0, "test_ReadWrite: output seek succeeded"); - } else { - bytes = bytes_read.value; - pos += bytes; - } - for (let i = 0; i < bytes; ++i) { - if (array[i] != array2[i]) { - ok( - false, - "Files do not match at position " + - i + - " (" + - array[i] + - "/" + - array2[i] + - ")" - ); - } - } - } - info("test_ReadWrite test complete"); - result = OS.Win.File.CloseHandle(input); - isnot(result, 0, "test_ReadWrite: inpout close succeeded"); - result = OS.Win.File.CloseHandle(output); - isnot(result, 0, "test_ReadWrite: outpout close succeeded"); - result = OS.Win.File.DeleteFile(output_name); - isnot(result, 0, "test_ReadWrite: output remove succeeded"); - info("test_ReadWrite cleanup complete"); -} - -function test_passing_undefined() { - info( - "Testing that an exception gets thrown when an FFI function is passed undefined" - ); - let exceptionRaised = false; - - try { - OS.Win.File.CreateFile( - undefined, - OS.Constants.Win.GENERIC_READ, - 0, - null, - OS.Constants.Win.OPEN_EXISTING, - 0, - null - ); - } catch (e) { - if (e instanceof TypeError && e.message.indexOf("CreateFile") > -1) { - exceptionRaised = true; - } else { - throw e; - } - } - - ok(exceptionRaised, "test_passing_undefined: exception gets thrown"); -}
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; - -module.exports = { - rules: { - "no-shadow": "off", - }, -};
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/head.js +++ /dev/null @@ -1,109 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -var { XPCOMUtils } = ChromeUtils.importESModule( - "resource://gre/modules/XPCOMUtils.sys.mjs" -); - -// Bug 1014484 can only be reproduced by loading OS.File first from the -// CommonJS loader, so we do not want OS.File to be loaded eagerly for -// all the tests in this directory. -ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm"); -ChromeUtils.defineESModuleGetters(this, { - FileUtils: "resource://gre/modules/FileUtils.sys.mjs", -}); -ChromeUtils.defineModuleGetter( - this, - "NetUtil", - "resource://gre/modules/NetUtil.jsm" -); - -Services.prefs.setBoolPref("toolkit.osfile.log", true); - -/** - * As add_task, but execute the test both with native operations and - * without. - */ -function add_test_pair(generator) { - add_task(async function() { - info("Executing test " + generator.name + " with native operations"); - Services.prefs.setBoolPref("toolkit.osfile.native", true); - return generator(); - }); - add_task(async function() { - info("Executing test " + generator.name + " without native operations"); - Services.prefs.setBoolPref("toolkit.osfile.native", false); - return generator(); - }); -} - -/** - * Fetch asynchronously the contents of a file using xpcom. - * - * Used for comparing xpcom-based results to os.file-based results. - * - * @param {string} path The _absolute_ path to the file. - * @return {promise} - * @resolves {string} The contents of the file. - */ -function reference_fetch_file(path, test) { - info("Fetching file " + path); - return new Promise((resolve, reject) => { - let file = new FileUtils.File(path); - NetUtil.asyncFetch( - { - uri: NetUtil.newURI(file), - loadUsingSystemPrincipal: true, - }, - function(stream, status) { - if (!Components.isSuccessCode(status)) { - reject(status); - return; - } - let result, reject; - try { - result = NetUtil.readInputStreamToString(stream, stream.available()); - } catch (x) { - reject = x; - } - stream.close(); - if (reject) { - reject(reject); - } else { - resolve(result); - } - } - ); - }); -} - -/** - * Compare asynchronously the contents two files using xpcom. - * - * Used for comparing xpcom-based results to os.file-based results. - * - * @param {string} a The _absolute_ path to the first file. - * @param {string} b The _absolute_ path to the second file. - * - * @resolves {null} - */ -function reference_compare_files(a, b, test) { - return (async function() { - info("Comparing files " + a + " and " + b); - let a_contents = await reference_fetch_file(a, test); - let b_contents = await reference_fetch_file(b, test); - Assert.equal(a_contents, b_contents); - })(); -} - -async function removeTestFile(filePath, ignoreNoSuchFile = true) { - try { - await OS.File.remove(filePath); - } catch (ex) { - if (!ignoreNoSuchFile || !ex.becauseNoSuchFile) { - do_throw(ex); - } - } -}
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_compression.js +++ /dev/null @@ -1,106 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -function run_test() { - do_test_pending(); - run_next_test(); -} - -add_task(async function test_compress_lz4() { - let path = OS.Path.join(OS.Constants.Path.tmpDir, "compression.lz"); - let length = 1024; - let array = new Uint8Array(length); - for (let i = 0; i < array.byteLength; ++i) { - array[i] = i; - } - let arrayAsString = Array.prototype.join.call(array); - - info("Writing data with lz4 compression"); - let bytes = await OS.File.writeAtomic(path, array, { compression: "lz4" }); - info("Compressed " + length + " bytes into " + bytes); - - info("Reading back with lz4 decompression"); - let decompressed = await OS.File.read(path, { compression: "lz4" }); - info("Decompressed into " + decompressed.byteLength + " bytes"); - Assert.equal(arrayAsString, Array.prototype.join.call(decompressed)); -}); - -add_task(async function test_uncompressed() { - info("Writing data without compression"); - let path = OS.Path.join(OS.Constants.Path.tmpDir, "no_compression.tmp"); - let array = new Uint8Array(1024); - for (let i = 0; i < array.byteLength; ++i) { - array[i] = i; - } - await OS.File.writeAtomic(path, array); // No compression - - let exn; - // Force decompression, reading should fail - try { - await OS.File.read(path, { compression: "lz4" }); - } catch (ex) { - exn = ex; - } - Assert.ok(!!exn); - // Check the exception message (and that it contains the file name) - Assert.ok( - exn.message.includes(`Invalid header (no magic number) - Data: ${path}`) - ); -}); - -add_task(async function test_no_header() { - let path = OS.Path.join(OS.Constants.Path.tmpDir, "no_header.tmp"); - let array = new Uint8Array(8).fill(0, 0); // Small array with no header - - info("Writing data with no header"); - - await OS.File.writeAtomic(path, array); // No compression - let exn; - // Force decompression, reading should fail - try { - await OS.File.read(path, { compression: "lz4" }); - } catch (ex) { - exn = ex; - } - Assert.ok(!!exn); - // Check the exception message (and that it contains the file name) - Assert.ok( - exn.message.includes(`Buffer is too short (no header) - Data: ${path}`) - ); -}); - -add_task(async function test_invalid_content() { - let path = OS.Path.join(OS.Constants.Path.tmpDir, "invalid_content.tmp"); - let arr1 = new Uint8Array([109, 111, 122, 76, 122, 52, 48, 0]); - let arr2 = new Uint8Array(248).fill(1, 0); - - let array = new Uint8Array(arr1.length + arr2.length); - array.set(arr1); - array.set(arr2, arr1.length); - - info("Writing invalid data (with a valid header and only ones after that)"); - - await OS.File.writeAtomic(path, array); // No compression - let exn; - // Force decompression, reading should fail - try { - await OS.File.read(path, { compression: "lz4" }); - } catch (ex) { - exn = ex; - } - Assert.ok(!!exn); - // Check the exception message (and that it contains the file name) - Assert.ok( - exn.message.includes( - `Invalid content: Decompression stopped at 0 - Data: ${path}` - ) - ); -}); - -add_task(function() { - do_test_finished(); -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_constants.js +++ /dev/null @@ -1,20 +0,0 @@ -"use strict"; - -const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -// Test that OS.Constants is defined correctly. -add_task(async function check_definition() { - Assert.ok(OS.Constants != null); - Assert.ok(!!OS.Constants.Win || !!OS.Constants.libc); - Assert.ok(OS.Constants.Path != null); - Assert.ok(OS.Constants.Sys != null); - // check system name - Assert.equal(Services.appinfo.OS, OS.Constants.Sys.Name); - - // check if using DEBUG build - if (Cc["@mozilla.org/xpcom/debug;1"].getService(Ci.nsIDebug2).isDebugBuild) { - Assert.ok(OS.Constants.Sys.DEBUG); - } else { - Assert.ok(typeof OS.Constants.Sys.DEBUG == "undefined"); - } -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_duration.js +++ /dev/null @@ -1,127 +0,0 @@ -var { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -/** - * Test optional duration reporting that can be used for telemetry. - */ -add_task(async function duration() { - const availableDurations = [ - "outSerializationDuration", - "outExecutionDuration", - ]; - Services.prefs.setBoolPref("toolkit.osfile.log", true); - // Options structure passed to a OS.File copy method. - let copyOptions = { - // These fields should be overwritten with the actual duration - // measurements. - outSerializationDuration: null, - outExecutionDuration: null, - }; - let currentDir = await OS.File.getCurrentDirectory(); - let pathSource = OS.Path.join(currentDir, "test_duration.js"); - let copyFile = pathSource + ".bak"; - function testOptions(options, name, durations = availableDurations) { - for (let duration of durations) { - info(`Checking ${duration} for operation: ${name}`); - info(`${name}: Gathered method duration time: ${options[duration]} ms`); - // Making sure that duration was updated. - Assert.equal(typeof options[duration], "number"); - Assert.ok(options[duration] >= 0); - } - } - - function testOptionIncrements( - options, - name, - backupDuration, - durations = availableDurations - ) { - for (let duration of durations) { - info(`Checking ${duration} increment for operation: ${name}`); - info(`${name}: Gathered method duration time: ${options[duration]} ms`); - info(`${name}: Previous duration: ${backupDuration[duration]} ms`); - // Making sure that duration was incremented. - Assert.ok(options[duration] >= backupDuration[duration]); - } - } - - // Testing duration of OS.File.copy. - await OS.File.copy(pathSource, copyFile, copyOptions); - testOptions(copyOptions, "OS.File.copy"); - await OS.File.remove(copyFile); - - // Trying an operation where options are cloned. - let pathDest = OS.Path.join( - OS.Constants.Path.tmpDir, - "osfile async test read writeAtomic.tmp" - ); - let tmpPath = pathDest + ".tmp"; - let readOptions = { - // We do not check for |outSerializationDuration| since |Scheduler.post| - // may not be called whenever |read| is called. - outExecutionDuration: null, - }; - let contents = await OS.File.read(pathSource, undefined, readOptions); - testOptions(readOptions, "OS.File.read", ["outExecutionDuration"]); - // Options structure passed to a OS.File writeAtomic method. - let writeAtomicOptions = { - // This field should be first initialized with the actual - // duration measurement then progressively incremented. - outExecutionDuration: null, - tmpPath, - }; - // Note that |contents| cannot be reused after this call since it is detached. - await OS.File.writeAtomic(pathDest, contents, writeAtomicOptions); - testOptions(writeAtomicOptions, "OS.File.writeAtomic", [ - "outExecutionDuration", - ]); - await OS.File.remove(pathDest); - - info( - `Ensuring that we can use ${availableDurations.join( - ", " - )} to accumulate durations` - ); - - let ARBITRARY_BASE_DURATION = 5; - copyOptions = { - // This field should now be incremented with the actual duration - // measurement. - outSerializationDuration: ARBITRARY_BASE_DURATION, - outExecutionDuration: ARBITRARY_BASE_DURATION, - }; - - // We need to copy the object, since having a reference would make this pointless. - let backupDuration = Object.assign({}, copyOptions); - - // Testing duration of OS.File.copy. - await OS.File.copy(pathSource, copyFile, copyOptions); - testOptionIncrements(copyOptions, "copy", backupDuration); - - backupDuration = Object.assign({}, copyOptions); - await OS.File.remove(copyFile, copyOptions); - testOptionIncrements(copyOptions, "remove", backupDuration); - - // Trying an operation where options are cloned. - // Options structure passed to a OS.File writeAtomic method. - writeAtomicOptions = { - // We do not check for |outSerializationDuration| since |Scheduler.post| - // may not be called whenever |writeAtomic| is called. - outExecutionDuration: ARBITRARY_BASE_DURATION, - }; - writeAtomicOptions.tmpPath = tmpPath; - backupDuration = Object.assign({}, writeAtomicOptions); - contents = await OS.File.read(pathSource, undefined, readOptions); - await OS.File.writeAtomic(pathDest, contents, writeAtomicOptions); - testOptionIncrements( - writeAtomicOptions, - "writeAtomicOptions", - backupDuration, - ["outExecutionDuration"] - ); - OS.File.remove(pathDest); - - // Testing an operation that doesn't take arguments at all - let file = await OS.File.open(pathSource); - await file.stat(); - await file.close(); -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_exception.js +++ /dev/null @@ -1,108 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Test that functions throw the appropriate exceptions. - */ - -"use strict"; - -var EXISTING_FILE = do_get_file("xpcshell.ini").path; - -// Tests on |open| - -add_test_pair(async function test_typeerror() { - let exn; - try { - let fd = await OS.File.open("/tmp", { no_such_key: 1 }); - info("Fd: " + fd); - } catch (ex) { - exn = ex; - } - info("Exception: " + exn); - Assert.ok(exn.constructor.name == "TypeError"); -}); - -// Tests on |read| - -add_test_pair(async function test_bad_encoding() { - info("Testing with a wrong encoding"); - try { - await OS.File.read(EXISTING_FILE, { encoding: "baby-speak-encoded" }); - do_throw("Should have thrown with an ex.becauseInvalidArgument"); - } catch (ex) { - if (ex.becauseInvalidArgument) { - info("Wrong encoding caused the correct exception"); - } else { - throw ex; - } - } - - try { - await OS.File.read(EXISTING_FILE, { encoding: 4 }); - do_throw("Should have thrown a TypeError"); - } catch (ex) { - if (ex.constructor.name == "TypeError") { - // Note that TypeError doesn't carry across compartments - info("Non-string encoding caused the correct exception"); - } else { - throw ex; - } - } -}); - -add_test_pair(async function test_bad_compression() { - info("Testing with a non-existing compression"); - try { - await OS.File.read(EXISTING_FILE, { compression: "mmmh-crunchy" }); - do_throw("Should have thrown with an ex.becauseInvalidArgument"); - } catch (ex) { - if (ex.becauseInvalidArgument) { - info("Wrong encoding caused the correct exception"); - } else { - throw ex; - } - } - - info("Testing with a bad type for option compression"); - try { - await OS.File.read(EXISTING_FILE, { compression: 5 }); - do_throw("Should have thrown a TypeError"); - } catch (ex) { - if (ex.constructor.name == "TypeError") { - // Note that TypeError doesn't carry across compartments - info("Non-string encoding caused the correct exception"); - } else { - throw ex; - } - } -}); - -add_test_pair(async function test_bad_bytes() { - info("Testing with a bad type for option bytes"); - try { - await OS.File.read(EXISTING_FILE, { bytes: "five" }); - do_throw("Should have thrown a TypeError"); - } catch (ex) { - if (ex.constructor.name == "TypeError") { - // Note that TypeError doesn't carry across compartments - info("Non-number bytes caused the correct exception"); - } else { - throw ex; - } - } -}); - -add_test_pair(async function read_non_existent() { - info("Testing with a non-existent file"); - try { - await OS.File.read("I/do/not/exist"); - do_throw("Should have thrown with an ex.becauseNoSuchFile"); - } catch (ex) { - if (ex.becauseNoSuchFile) { - info("Correct exceptions"); - } else { - throw ex; - } - } -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_file_URL_conversion.js +++ /dev/null @@ -1,119 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -function run_test() { - const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - const { FileUtils } = ChromeUtils.importESModule( - "resource://gre/modules/FileUtils.sys.mjs" - ); - - let isWindows = "@mozilla.org/windows-registry-key;1" in Cc; - - // Test cases for filePathToURI - let paths = isWindows - ? [ - "C:\\", - "C:\\test", - "C:\\test\\", - "C:\\test%2f", - "C:\\test\\test\\test", - "C:\\test;+%", - "C:\\test?action=index\\", - "C:\\test test", - "\\\\C:\\a\\b\\c", - "\\\\Server\\a\\b\\c", - - // note that per http://support.microsoft.com/kb/177506 (under more info), - // the following characters are allowed on Windows: - "C:\\char^", - "C:\\char&", - "C:\\char'", - "C:\\char@", - "C:\\char{", - "C:\\char}", - "C:\\char[", - "C:\\char]", - "C:\\char,", - "C:\\char$", - "C:\\char=", - "C:\\char!", - "C:\\char-", - "C:\\char#", - "C:\\char(", - "C:\\char)", - "C:\\char%", - "C:\\char.", - "C:\\char+", - "C:\\char~", - "C:\\char_", - ] - : [ - "/", - "/test", - "/test/", - "/test%2f", - "/test/test/test", - "/test;+%", - "/test?action=index/", - "/test test", - "/punctuation/;,/?:@&=+$-_.!~*'()[]\"#", - "/CasePreserving", - ]; - - // some additional URIs to test, beyond those generated from paths - let uris = isWindows - ? [ - "file:///C:/test/", - "file://localhost/C:/test", - "file:///c:/test/test.txt", - // 'file:///C:/foo%2f', // trailing, encoded slash - "file:///C:/%3f%3F", - "file:///C:/%3b%3B", - "file:///C:/%3c%3C", // not one of the special-cased ? or ; - "file:///C:/%78", // 'x', not usually uri encoded - "file:///C:/test#frag", // a fragment identifier - "file:///C:/test?action=index", // an actual query component - ] - : [ - "file:///test/", - "file://localhost/test", - "file:///test/test.txt", - "file:///foo%2f", // trailing, encoded slash - "file:///%3f%3F", - "file:///%3b%3B", - "file:///%3c%3C", // not one of the special-cased ? or ; - "file:///%78", // 'x', not usually uri encoded - "file:///test#frag", // a fragment identifier - "file:///test?action=index", // an actual query component - ]; - - for (let path of paths) { - // convert that to a uri using FileUtils and Services, which toFileURI is trying to model - let file = FileUtils.File(path); - let uri = Services.io.newFileURI(file).spec; - Assert.equal(uri, OS.Path.toFileURI(path)); - - // keep the resulting URI to try the reverse, except for "C:\" for which the - // behavior of nsIFileURL and OS.File is inconsistent - if (path != "C:\\") { - uris.push(uri); - } - } - - for (let uri of uris) { - // convert URIs to paths with nsIFileURI, which fromFileURI is trying to model - let path = Services.io.newURI(uri).QueryInterface(Ci.nsIFileURL).file.path; - Assert.equal(path, OS.Path.fromFileURI(uri)); - } - - // check that non-file URLs aren't allowed - let thrown = false; - try { - OS.Path.fromFileURI("http://test.com"); - } catch (e) { - Assert.equal(e.message, "fromFileURI expects a file URI"); - thrown = true; - } - Assert.ok(thrown); -}
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_logging.js +++ /dev/null @@ -1,73 +0,0 @@ -"use strict"; - -const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -/** - * Tests logging by passing OS.Shared.LOG both an object with its own - * toString method, and one with the default. - */ -function run_test() { - do_test_pending(); - let messageCount = 0; - - info("Test starting"); - - // Create a console listener. - let consoleListener = { - observe(aMessage) { - // Ignore unexpected messages. - if (!(aMessage instanceof Ci.nsIConsoleMessage)) { - return; - } - // This is required, as printing to the |Services.console| - // while in the observe function causes an exception. - executeSoon(function() { - info("Observing message " + aMessage.message); - if (!aMessage.message.includes("TEST OS")) { - return; - } - - ++messageCount; - if (messageCount == 1) { - Assert.equal(aMessage.message, 'TEST OS {"name":"test"}\n'); - } - if (messageCount == 2) { - Assert.equal(aMessage.message, "TEST OS name is test\n"); - toggleConsoleListener(false); - do_test_finished(); - } - }); - }, - }; - - // Set/Unset the console listener. - function toggleConsoleListener(pref) { - info("Setting console listener: " + pref); - Services.prefs.setBoolPref("toolkit.osfile.log", pref); - Services.prefs.setBoolPref("toolkit.osfile.log.redirect", pref); - Services.console[pref ? "registerListener" : "unregisterListener"]( - consoleListener - ); - } - - toggleConsoleListener(true); - - let objectDefault = { name: "test" }; - let CustomToString = function() { - this.name = "test"; - }; - CustomToString.prototype.toString = function() { - return "name is " + this.name; - }; - let objectCustom = new CustomToString(); - - info(OS.Shared.LOG.toSource()); - - info("Logging 1"); - OS.Shared.LOG(objectDefault); - - info("Logging 2"); - OS.Shared.LOG(objectCustom); - // Once both messages are observed OS.Shared.DEBUG, and OS.Shared.TEST - // are reset to false. -}
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_makeDir.js +++ /dev/null @@ -1,137 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -var Path = OS.Path; -var profileDir; - -registerCleanupFunction(function() { - Services.prefs.setBoolPref("toolkit.osfile.log", false); -}); - -/** - * Test OS.File.makeDir - */ - -add_task(function init() { - // Set up profile. We create the directory in the profile, because the profile - // is removed after every test run. - do_get_profile(); - profileDir = OS.Constants.Path.profileDir; - Services.prefs.setBoolPref("toolkit.osfile.log", true); -}); - -/** - * Basic use - */ - -add_task(async function test_basic() { - let dir = Path.join(profileDir, "directory"); - - // Sanity checking for the test - Assert.equal(false, await OS.File.exists(dir)); - - // Make a directory - await OS.File.makeDir(dir); - - // check if the directory exists - await OS.File.stat(dir); - - // Make a directory that already exists, this should succeed - await OS.File.makeDir(dir); - - // Make a directory with ignoreExisting - await OS.File.makeDir(dir, { ignoreExisting: true }); - - // Make a directory with ignoreExisting false - let exception = null; - try { - await OS.File.makeDir(dir, { ignoreExisting: false }); - } catch (ex) { - exception = ex; - } - - Assert.ok(!!exception); - Assert.ok(exception instanceof OS.File.Error); - Assert.ok(exception.becauseExists); -}); - -// Make a root directory that already exists -add_task(async function test_root() { - if (OS.Constants.Win) { - await OS.File.makeDir("C:"); - await OS.File.makeDir("C:\\"); - } else { - await OS.File.makeDir("/"); - } -}); - -/** - * Creating subdirectories - */ -add_task(async function test_option_from() { - let dir = Path.join(profileDir, "a", "b", "c"); - - // Sanity checking for the test - Assert.equal(false, await OS.File.exists(dir)); - - // Make a directory - await OS.File.makeDir(dir, { from: profileDir }); - - // check if the directory exists - await OS.File.stat(dir); - - // Make a directory that already exists, this should succeed - await OS.File.makeDir(dir); - - // Make a directory with ignoreExisting - await OS.File.makeDir(dir, { ignoreExisting: true }); - - // Make a directory with ignoreExisting false - let exception = null; - try { - await OS.File.makeDir(dir, { ignoreExisting: false }); - } catch (ex) { - exception = ex; - } - - Assert.ok(!!exception); - Assert.ok(exception instanceof OS.File.Error); - Assert.ok(exception.becauseExists); - - // Make a directory without |from| and fail - let dir2 = Path.join(profileDir, "g", "h", "i"); - exception = null; - try { - await OS.File.makeDir(dir2); - } catch (ex) { - exception = ex; - } - - Assert.ok(!!exception); - Assert.ok(exception instanceof OS.File.Error); - Assert.ok(exception.becauseNoSuchFile); - - // Test edge cases on paths - - let dir3 = Path.join(profileDir, "d", "", "e", "f"); - Assert.equal(false, await OS.File.exists(dir3)); - await OS.File.makeDir(dir3, { from: profileDir }); - Assert.ok(await OS.File.exists(dir3)); - - let dir4; - if (OS.Constants.Win) { - // Test that we can create a directory recursively even - // if we have too many "\\". - dir4 = profileDir + "\\\\g"; - } else { - dir4 = profileDir + "////g"; - } - Assert.equal(false, await OS.File.exists(dir4)); - await OS.File.makeDir(dir4, { from: profileDir }); - Assert.ok(await OS.File.exists(dir4)); -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_open.js +++ /dev/null @@ -1,75 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -/** - * Test OS.File.open for reading: - * - with an existing file (should succeed); - * - with a non-existing file (should fail); - * - with inconsistent arguments (should fail). - */ -add_task(async function() { - // Attempt to open a file that does not exist, ensure that it yields the - // appropriate error. - try { - await OS.File.open(OS.Path.join(".", "This file does not exist")); - Assert.ok(false, "File opening 1 succeeded (it should fail)"); - } catch (err) { - if (err instanceof OS.File.Error && err.becauseNoSuchFile) { - info("File opening 1 failed " + err); - } else { - throw err; - } - } - // Attempt to open a file with the wrong args, so that it fails before - // serialization, ensure that it yields the appropriate error. - info("Attempting to open a file with wrong arguments"); - try { - let fd = await OS.File.open(1, 2, 3); - Assert.ok(false, "File opening 2 succeeded (it should fail)" + fd); - } catch (err) { - info("File opening 2 failed " + err); - Assert.equal( - false, - err instanceof OS.File.Error, - "File opening 2 returned something that is not a file error" - ); - Assert.ok( - err.constructor.name == "TypeError", - "File opening 2 returned a TypeError" - ); - } - - // Attempt to open a file correctly - info("Attempting to open a file correctly"); - let openedFile = await OS.File.open( - OS.Path.join(do_get_cwd().path, "test_open.js") - ); - info("File opened correctly"); - - info("Attempting to close a file correctly"); - await openedFile.close(); - - info("Attempting to close a file again"); - await openedFile.close(); -}); - -/** - * Test the error thrown by OS.File.open when attempting to open a directory - * that does not exist. - */ -add_task(async function test_error_attributes() { - let dir = OS.Path.join(do_get_profile().path, "test_osfileErrorAttrs"); - let fpath = OS.Path.join(dir, "test_error_attributes.txt"); - - try { - await OS.File.open(fpath, { truncate: true }, {}); - Assert.ok(false, "Opening path suceeded (it should fail) " + fpath); - } catch (err) { - Assert.ok(err instanceof OS.File.Error); - Assert.ok(err.becauseNoSuchFile); - } -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_osfile_async.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; - -const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -/** - * A trivial test ensuring that we can call osfile from xpcshell. - * (see bug 808161) - */ - -function run_test() { - do_test_pending(); - OS.File.getCurrentDirectory().then(do_test_finished, do_test_finished); -}
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_osfile_async_append.js +++ /dev/null @@ -1,105 +0,0 @@ -"use strict"; - -info("starting tests"); - -const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -/** - * A test to check that the |append| mode flag is correctly implemented. - * (see bug 925865) - */ - -function setup_mode(mode) { - // Complete mode. - let realMode = { - read: true, - write: true, - }; - for (let k in mode) { - realMode[k] = mode[k]; - } - return realMode; -} - -// Test append mode. -async function test_append(mode) { - let path = OS.Path.join( - OS.Constants.Path.tmpDir, - "test_osfile_async_append.tmp" - ); - - // Clear any left-over files from previous runs. - await removeTestFile(path); - - try { - mode = setup_mode(mode); - mode.append = true; - if (mode.trunc) { - // Pre-fill file with some data to see if |trunc| actually works. - await OS.File.writeAtomic(path, new Uint8Array(500)); - } - let file = await OS.File.open(path, mode); - try { - await file.write(new Uint8Array(1000)); - await file.setPosition(0, OS.File.POS_START); - await file.read(100); - // Should be at offset 100, length 1000 now. - await file.write(new Uint8Array(100)); - // Should be at offset 1100, length 1100 now. - let stat = await file.stat(); - Assert.equal(1100, stat.size); - } finally { - await file.close(); - } - } catch (ex) { - await removeTestFile(path); - } -} - -// Test no-append mode. -async function test_no_append(mode) { - let path = OS.Path.join( - OS.Constants.Path.tmpDir, - "test_osfile_async_noappend.tmp" - ); - - // Clear any left-over files from previous runs. - await removeTestFile(path); - - try { - mode = setup_mode(mode); - mode.append = false; - if (mode.trunc) { - // Pre-fill file with some data to see if |trunc| actually works. - await OS.File.writeAtomic(path, new Uint8Array(500)); - } - let file = await OS.File.open(path, mode); - try { - await file.write(new Uint8Array(1000)); - await file.setPosition(0, OS.File.POS_START); - await file.read(100); - // Should be at offset 100, length 1000 now. - await file.write(new Uint8Array(100)); - // Should be at offset 200, length 1000 now. - let stat = await file.stat(); - Assert.equal(1000, stat.size); - } finally { - await file.close(); - } - } finally { - await removeTestFile(path); - } -} - -var test_flags = [{}, { create: true }, { trunc: true }]; -function run_test() { - do_test_pending(); - - for (let t of test_flags) { - add_task(test_append.bind(null, t)); - add_task(test_no_append.bind(null, t)); - } - add_task(do_test_finished); - - run_next_test(); -}
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_osfile_async_bytes.js +++ /dev/null @@ -1,40 +0,0 @@ -"use strict"; - -const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -function run_test() { - do_test_pending(); - run_next_test(); -} - -/** - * Test to ensure that {bytes:} in options to |write| is correctly - * preserved. - */ -add_task(async function test_bytes() { - let path = OS.Path.join( - OS.Constants.Path.tmpDir, - "test_osfile_async_bytes.tmp" - ); - let file = await OS.File.open(path, { trunc: true, read: true, write: true }); - try { - try { - // 1. Test write, by supplying {bytes:} options smaller than the actual - // buffer. - await file.write(new Uint8Array(2048), { bytes: 1024 }); - Assert.equal((await file.stat()).size, 1024); - - // 2. Test that passing nullish values for |options| still works. - await file.setPosition(0, OS.File.POS_END); - await file.write(new Uint8Array(1024), null); - await file.write(new Uint8Array(1024), undefined); - Assert.equal((await file.stat()).size, 3072); - } finally { - await file.close(); - } - } finally { - await OS.File.remove(path); - } -}); - -add_task(do_test_finished);
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_osfile_async_copy.js +++ /dev/null @@ -1,109 +0,0 @@ -"use strict"; - -const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); -const { FileUtils } = ChromeUtils.importESModule( - "resource://gre/modules/FileUtils.sys.mjs" -); -const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); - -function run_test() { - do_test_pending(); - run_next_test(); -} - -/** - * A file that we know exists and that can be used for reading. - */ -var EXISTING_FILE = "test_osfile_async_copy.js"; - -/** - * Fetch asynchronously the contents of a file using xpcom. - * - * Used for comparing xpcom-based results to os.file-based results. - * - * @param {string} path The _absolute_ path to the file. - * @return {promise} - * @resolves {string} The contents of the file. - */ -var reference_fetch_file = function reference_fetch_file(path) { - return new Promise((resolve, reject) => { - let file = new FileUtils.File(path); - NetUtil.asyncFetch( - { - uri: NetUtil.newURI(file), - loadUsingSystemPrincipal: true, - }, - function(stream, status) { - if (!Components.isSuccessCode(status)) { - reject(status); - return; - } - let result, reject; - try { - result = NetUtil.readInputStreamToString(stream, stream.available()); - } catch (x) { - reject = x; - } - stream.close(); - if (reject) { - reject(reject); - } else { - resolve(result); - } - } - ); - }); -}; - -/** - * Compare asynchronously the contents two files using xpcom. - * - * Used for comparing xpcom-based results to os.file-based results. - * - * @param {string} a The _absolute_ path to the first file. - * @param {string} b The _absolute_ path to the second file. - * - * @resolves {null} - */ -var reference_compare_files = async function reference_compare_files(a, b) { - let a_contents = await reference_fetch_file(a); - let b_contents = await reference_fetch_file(b); - // Not using do_check_eq to avoid dumping the whole file to the log. - // It is OK to === compare here, as both variables contain a string. - Assert.ok(a_contents === b_contents); -}; - -/** - * Test to ensure that OS.File.copy works. - */ -async function test_copymove(options = {}) { - let source = OS.Path.join(await OS.File.getCurrentDirectory(), EXISTING_FILE); - let dest = OS.Path.join( - OS.Constants.Path.tmpDir, - "test_osfile_async_copy_dest.tmp" - ); - let dest2 = OS.Path.join( - OS.Constants.Path.tmpDir, - "test_osfile_async_copy_dest2.tmp" - ); - try { - // 1. Test copy. - await OS.File.copy(source, dest, options); - await reference_compare_files(source, dest); - // 2. Test subsequent move. - await OS.File.move(dest, dest2); - await reference_compare_files(source, dest2); - // 3. Check that the moved file was really moved. - Assert.equal(await OS.File.exists(dest), false); - } finally { - await removeTestFile(dest); - await removeTestFile(dest2); - } -} - -// Regular copy test. -add_task(test_copymove); -// Userland copy test. -add_task(test_copymove.bind(null, { unixUserland: true })); - -add_task(do_test_finished);
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_osfile_async_flush.js +++ /dev/null @@ -1,31 +0,0 @@ -"use strict"; - -const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -function run_test() { - do_test_pending(); - run_next_test(); -} - -/** - * Test to ensure that |File.prototype.flush| is available in the async API. - */ - -add_task(async function test_flush() { - let path = OS.Path.join( - OS.Constants.Path.tmpDir, - "test_osfile_async_flush.tmp" - ); - let file = await OS.File.open(path, { trunc: true, write: true }); - try { - try { - await file.flush(); - } finally { - await file.close(); - } - } finally { - await OS.File.remove(path); - } -}); - -add_task(do_test_finished);
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_osfile_async_largefiles.js +++ /dev/null @@ -1,137 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -const { ctypes } = ChromeUtils.importESModule( - "resource://gre/modules/ctypes.sys.mjs" -); -const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -/** - * A test to check that .getPosition/.setPosition work with large files. - * (see bug 952997) - */ - -// Test setPosition/getPosition. -async function test_setPosition(forward, current, backward) { - let path = OS.Path.join( - OS.Constants.Path.tmpDir, - "test_osfile_async_largefiles.tmp" - ); - - // Clear any left-over files from previous runs. - await removeTestFile(path); - - try { - let file = await OS.File.open(path, { write: true, append: false }); - try { - let pos = 0; - - // 1. seek forward from start - info("Moving forward: " + forward); - await file.setPosition(forward, OS.File.POS_START); - pos += forward; - Assert.equal(await file.getPosition(), pos); - - // 2. seek forward from current position - info("Moving current: " + current); - await file.setPosition(current, OS.File.POS_CURRENT); - pos += current; - Assert.equal(await file.getPosition(), pos); - - // 3. seek backward from current position - info("Moving current backward: " + backward); - await file.setPosition(-backward, OS.File.POS_CURRENT); - pos -= backward; - Assert.equal(await file.getPosition(), pos); - } finally { - await file.setPosition(0, OS.File.POS_START); - await file.close(); - } - } catch (ex) { - await removeTestFile(path); - } -} - -// Test setPosition/getPosition expected failures. -async function test_setPosition_failures() { - let path = OS.Path.join( - OS.Constants.Path.tmpDir, - "test_osfile_async_largefiles.tmp" - ); - - // Clear any left-over files from previous runs. - await removeTestFile(path); - - try { - let file = await OS.File.open(path, { write: true, append: false }); - try { - // 1. Use an invalid position value - try { - await file.setPosition(0.5, OS.File.POS_START); - do_throw("Shouldn't have succeeded"); - } catch (ex) { - Assert.ok(ex.toString().includes("can't pass")); - } - // Since setPosition should have bailed, it shouldn't have moved the - // file pointer at all. - Assert.equal(await file.getPosition(), 0); - - // 2. Use an invalid position value - try { - await file.setPosition(0xffffffff + 0.5, OS.File.POS_START); - do_throw("Shouldn't have succeeded"); - } catch (ex) { - Assert.ok(ex.toString().includes("can't pass")); - } - // Since setPosition should have bailed, it shouldn't have moved the - // file pointer at all. - Assert.equal(await file.getPosition(), 0); - - // 3. Use a position that cannot be represented as a double - try { - // Not all numbers after 9007199254740992 can be represented as a - // double. E.g. in js 9007199254740992 + 1 == 9007199254740992 - await file.setPosition(9007199254740992, OS.File.POS_START); - await file.setPosition(1, OS.File.POS_CURRENT); - do_throw("Shouldn't have succeeded"); - } catch (ex) { - info(ex.toString()); - Assert.ok(!!ex); - } - } finally { - await file.setPosition(0, OS.File.POS_START); - await file.close(); - await removeTestFile(path); - } - } catch (ex) { - do_throw(ex); - } -} - -function run_test() { - // First verify stuff works for small values. - add_task(test_setPosition.bind(null, 0, 100, 50)); - add_task(test_setPosition.bind(null, 1000, 100, 50)); - add_task(test_setPosition.bind(null, 1000, -100, -50)); - - if (OS.Constants.Win || ctypes.off_t.size >= 8) { - // Now verify stuff still works for large values. - // 1. Multiple small seeks, which add up to > MAXINT32 - add_task(test_setPosition.bind(null, 0x7fffffff, 0x7fffffff, 0)); - // 2. Plain large seek, that should end up at 0 again. - // 0xffffffff also happens to be the INVALID_SET_FILE_POINTER value on - // Windows, so this also tests the error handling - add_task(test_setPosition.bind(null, 0, 0xffffffff, 0xffffffff)); - // 3. Multiple large seeks that should end up > MAXINT32. - add_task(test_setPosition.bind(null, 0xffffffff, 0xffffffff, 0xffffffff)); - // 5. Multiple large seeks with negative offsets. - add_task(test_setPosition.bind(null, 0xffffffff, -0x7fffffff, 0x7fffffff)); - - // 6. Check failures - add_task(test_setPosition_failures); - } - - run_next_test(); -}
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_osfile_async_setDates.js +++ /dev/null @@ -1,214 +0,0 @@ -"use strict"; - -/* eslint-disable no-lone-blocks */ - -const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -/** - * A test to ensure that OS.File.setDates and OS.File.prototype.setDates are - * working correctly. - * (see bug 924916) - */ - -// Non-prototypical tests, operating on path names. -add_task(async function test_nonproto() { - // First, create a file we can mess with. - let path = OS.Path.join( - OS.Constants.Path.tmpDir, - "test_osfile_async_setDates_nonproto.tmp" - ); - await OS.File.writeAtomic(path, new Uint8Array(1)); - - try { - // 1. Try to set some well known dates. - // We choose multiples of 2000ms, because the time stamp resolution of - // the underlying OS might not support something more precise. - const accDate = 2000; - const modDate = 4000; - { - await OS.File.setDates(path, accDate, modDate); - let stat = await OS.File.stat(path); - Assert.equal(accDate, stat.lastAccessDate.getTime()); - Assert.equal(modDate, stat.lastModificationDate.getTime()); - } - - // 2.1 Try to omit modificationDate (which should then default to - // |Date.now()|, expect for resolution differences). - { - await OS.File.setDates(path, accDate); - let stat = await OS.File.stat(path); - Assert.equal(accDate, stat.lastAccessDate.getTime()); - Assert.notEqual(modDate, stat.lastModificationDate.getTime()); - } - - // 2.2 Try to omit accessDate as well (which should then default to - // |Date.now()|, expect for resolution differences). - { - await OS.File.setDates(path); - let stat = await OS.File.stat(path); - Assert.notEqual(accDate, stat.lastAccessDate.getTime()); - Assert.notEqual(modDate, stat.lastModificationDate.getTime()); - } - - // 3. Repeat 1., but with Date objects this time - { - await OS.File.setDates(path, new Date(accDate), new Date(modDate)); - let stat = await OS.File.stat(path); - Assert.equal(accDate, stat.lastAccessDate.getTime()); - Assert.equal(modDate, stat.lastModificationDate.getTime()); - } - - // 4. Check that invalid params will cause an exception/rejection. - { - for (let p of ["invalid", new Uint8Array(1), NaN]) { - try { - await OS.File.setDates(path, p, modDate); - do_throw("Invalid access date should have thrown for: " + p); - } catch (ex) { - let stat = await OS.File.stat(path); - Assert.equal(accDate, stat.lastAccessDate.getTime()); - Assert.equal(modDate, stat.lastModificationDate.getTime()); - } - try { - await OS.File.setDates(path, accDate, p); - do_throw("Invalid modification date should have thrown for: " + p); - } catch (ex) { - let stat = await OS.File.stat(path); - Assert.equal(accDate, stat.lastAccessDate.getTime()); - Assert.equal(modDate, stat.lastModificationDate.getTime()); - } - try { - await OS.File.setDates(path, p, p); - do_throw("Invalid dates should have thrown for: " + p); - } catch (ex) { - let stat = await OS.File.stat(path); - Assert.equal(accDate, stat.lastAccessDate.getTime()); - Assert.equal(modDate, stat.lastModificationDate.getTime()); - } - } - } - } finally { - // Remove the temp file again - await OS.File.remove(path); - } -}); - -// Prototypical tests, operating on |File| handles. -add_task(async function test_proto() { - if (OS.Constants.Sys.Name == "Android") { - info("File.prototype.setDates is not implemented for Android"); - Assert.equal(OS.File.prototype.setDates, undefined); - return; - } - - // First, create a file we can mess with. - let path = OS.Path.join( - OS.Constants.Path.tmpDir, - "test_osfile_async_setDates_proto.tmp" - ); - await OS.File.writeAtomic(path, new Uint8Array(1)); - - try { - let fd = await OS.File.open(path, { write: true }); - - try { - // 1. Try to set some well known dates. - // We choose multiples of 2000ms, because the time stamp resolution of - // the underlying OS might not support something more precise. - const accDate = 2000; - const modDate = 4000; - { - await fd.setDates(accDate, modDate); - let stat = await fd.stat(); - Assert.equal(accDate, stat.lastAccessDate.getTime()); - Assert.equal(modDate, stat.lastModificationDate.getTime()); - } - - // 2.1 Try to omit modificationDate (which should then default to - // |Date.now()|, expect for resolution differences). - { - await fd.setDates(accDate); - let stat = await fd.stat(); - Assert.equal(accDate, stat.lastAccessDate.getTime()); - Assert.notEqual(modDate, stat.lastModificationDate.getTime()); - } - - // 2.2 Try to omit accessDate as well (which should then default to - // |Date.now()|, expect for resolution differences). - { - await fd.setDates(); - let stat = await fd.stat(); - Assert.notEqual(accDate, stat.lastAccessDate.getTime()); - Assert.notEqual(modDate, stat.lastModificationDate.getTime()); - } - - // 3. Repeat 1., but with Date objects this time - { - await fd.setDates(new Date(accDate), new Date(modDate)); - let stat = await fd.stat(); - Assert.equal(accDate, stat.lastAccessDate.getTime()); - Assert.equal(modDate, stat.lastModificationDate.getTime()); - } - - // 4. Check that invalid params will cause an exception/rejection. - { - for (let p of ["invalid", new Uint8Array(1), NaN]) { - try { - await fd.setDates(p, modDate); - do_throw("Invalid access date should have thrown for: " + p); - } catch (ex) { - let stat = await fd.stat(); - Assert.equal(accDate, stat.lastAccessDate.getTime()); - Assert.equal(modDate, stat.lastModificationDate.getTime()); - } - try { - await fd.setDates(accDate, p); - do_throw("Invalid modification date should have thrown for: " + p); - } catch (ex) { - let stat = await fd.stat(); - Assert.equal(accDate, stat.lastAccessDate.getTime()); - Assert.equal(modDate, stat.lastModificationDate.getTime()); - } - try { - await fd.setDates(p, p); - do_throw("Invalid dates should have thrown for: " + p); - } catch (ex) { - let stat = await fd.stat(); - Assert.equal(accDate, stat.lastAccessDate.getTime()); - Assert.equal(modDate, stat.lastModificationDate.getTime()); - } - } - } - } finally { - await fd.close(); - } - } finally { - // Remove the temp file again - await OS.File.remove(path); - } -}); - -// Tests setting dates on directories. -add_task(async function test_dirs() { - let path = OS.Path.join( - OS.Constants.Path.tmpDir, - "test_osfile_async_setDates_dir" - ); - await OS.File.makeDir(path); - - try { - // 1. Try to set some well known dates. - // We choose multiples of 2000ms, because the time stamp resolution of - // the underlying OS might not support something more precise. - const accDate = 2000; - const modDate = 4000; - { - await OS.File.setDates(path, accDate, modDate); - let stat = await OS.File.stat(path); - Assert.equal(accDate, stat.lastAccessDate.getTime()); - Assert.equal(modDate, stat.lastModificationDate.getTime()); - } - } finally { - await OS.File.removeEmptyDir(path); - } -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_osfile_async_setPermissions.js +++ /dev/null @@ -1,102 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -/** - * A test to ensure that OS.File.setPermissions and - * OS.File.prototype.setPermissions are all working correctly. - * (see bug 1001849) - * These functions are currently Unix-specific. The manifest skips - * the test on Windows. - */ - -/** - * Helper function for test logging: prints a POSIX file permission mode as an - * octal number, with a leading '0' per C (not JS) convention. When the - * numeric value is 0o777 or lower, it is padded on the left with zeroes to - * four digits wide. - * Sample outputs: 0022, 0644, 04755. - */ -function format_mode(mode) { - if (mode <= 0o777) { - return ("0000" + mode.toString(8)).slice(-4); - } - return "0" + mode.toString(8); -} - -const _umask = OS.Constants.Sys.umask; -info("umask: " + format_mode(_umask)); - -/** - * Compute the mode that a file should have after applying the umask, - * whatever it happens to be. - */ -function apply_umask(mode) { - return mode & ~_umask; -} - -// Sequence of setPermission parameters and expected file mode. The first test -// checks the permissions when the file is first created. -var testSequence = [ - [null, apply_umask(0o600)], - [{ unixMode: 0o4777 }, apply_umask(0o4777)], - [{ unixMode: 0o4777, unixHonorUmask: false }, 0o4777], - [{ unixMode: 0o4777, unixHonorUmask: true }, apply_umask(0o4777)], - [undefined, apply_umask(0o600)], - [{ unixMode: 0o666 }, apply_umask(0o666)], - [{ unixMode: 0o600 }, apply_umask(0o600)], - [{ unixMode: 0 }, 0], - [{}, apply_umask(0o600)], -]; - -// Test application to paths. -add_task(async function test_path_setPermissions() { - let path = OS.Path.join( - OS.Constants.Path.tmpDir, - "test_osfile_async_setPermissions_path.tmp" - ); - await OS.File.writeAtomic(path, new Uint8Array(1)); - - try { - for (let [options, expectedMode] of testSequence) { - if (options !== null) { - info("Setting permissions to " + JSON.stringify(options)); - await OS.File.setPermissions(path, options); - } - - let stat = await OS.File.stat(path); - Assert.equal(format_mode(stat.unixMode), format_mode(expectedMode)); - } - } finally { - await OS.File.remove(path); - } -}); - -// Test application to open files. -add_task(async function test_file_setPermissions() { - let path = OS.Path.join( - OS.Constants.Path.tmpDir, - "test_osfile_async_setPermissions_file.tmp" - ); - await OS.File.writeAtomic(path, new Uint8Array(1)); - - try { - let fd = await OS.File.open(path, { write: true }); - try { - for (let [options, expectedMode] of testSequence) { - if (options !== null) { - info("Setting permissions to " + JSON.stringify(options)); - await fd.setPermissions(options); - } - - let stat = await fd.stat(); - Assert.equal(format_mode(stat.unixMode), format_mode(expectedMode)); - } - } finally { - await fd.close(); - } - } finally { - await OS.File.remove(path); - } -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_osfile_closed.js +++ /dev/null @@ -1,46 +0,0 @@ -"use strict"; - -const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -function run_test() { - do_test_pending(); - run_next_test(); -} - -add_task(async function test_closed() { - OS.Shared.DEBUG = true; - let currentDir = await OS.File.getCurrentDirectory(); - info("Open a file, ensure that we can call stat()"); - let path = OS.Path.join(currentDir, "test_osfile_closed.js"); - let file = await OS.File.open(path); - await file.stat(); - Assert.ok(true); - - await file.close(); - - info("Ensure that we cannot stat() on closed file"); - let exn; - try { - await file.stat(); - } catch (ex) { - exn = ex; - } - info("Ensure that this raises the correct error"); - Assert.ok(!!exn); - Assert.ok(exn instanceof OS.File.Error); - Assert.ok(exn.becauseClosed); - - info("Ensure that we cannot read() on closed file"); - exn = null; - try { - await file.read(); - } catch (ex) { - exn = ex; - } - info("Ensure that this raises the correct error"); - Assert.ok(!!exn); - Assert.ok(exn instanceof OS.File.Error); - Assert.ok(exn.becauseClosed); -}); - -add_task(do_test_finished);
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_osfile_error.js +++ /dev/null @@ -1,56 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -var { - OS: { File, Path, Constants }, -} = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -add_task(async function testFileError_with_writeAtomic() { - let DEFAULT_CONTENTS = "default contents" + Math.random(); - let path = Path.join(Constants.Path.tmpDir, "testFileError.tmp"); - await File.remove(path); - await File.writeAtomic(path, DEFAULT_CONTENTS); - let exception; - try { - await File.writeAtomic(path, DEFAULT_CONTENTS, { noOverwrite: true }); - } catch (ex) { - exception = ex; - } - Assert.ok(exception instanceof File.Error); - Assert.ok(exception.path == path); -}); - -add_task(async function testFileError_with_makeDir() { - let path = Path.join(Constants.Path.tmpDir, "directory"); - await File.removeDir(path); - await File.makeDir(path); - let exception; - try { - await File.makeDir(path, { ignoreExisting: false }); - } catch (ex) { - exception = ex; - } - Assert.ok(exception instanceof File.Error); - Assert.ok(exception.path == path); -}); - -add_task(async function testFileError_with_move() { - let DEFAULT_CONTENTS = "default contents" + Math.random(); - let sourcePath = Path.join(Constants.Path.tmpDir, "src.tmp"); - let destPath = Path.join(Constants.Path.tmpDir, "dest.tmp"); - await File.remove(sourcePath); - await File.remove(destPath); - await File.writeAtomic(sourcePath, DEFAULT_CONTENTS); - await File.writeAtomic(destPath, DEFAULT_CONTENTS); - let exception; - try { - await File.move(sourcePath, destPath, { noOverwrite: true }); - } catch (ex) { - exception = ex; - } - info(exception); - Assert.ok(exception instanceof File.Error); - Assert.ok(exception.path == sourcePath); -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_osfile_kill.js +++ /dev/null @@ -1,97 +0,0 @@ -"use strict"; - -const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -// We want the actual global to get at the internals since Scheduler is not -// exported. -var { Scheduler } = ChromeUtils.import( - "resource://gre/modules/osfile/osfile_async_front.jsm" -); - -/** - * Verify that Scheduler.kill() interacts with other OS.File requests correctly, - * and that no requests are lost. This is relevant because on B2G we - * auto-kill the worker periodically, making it very possible for valid requests - * to be interleaved with the automatic kill(). - * - * This test is being created with the fix for Bug 1125989 where `kill` queue - * management was found to be buggy. It is a glass-box test that explicitly - * re-creates the observed failure situation; it is not guaranteed to prevent - * all future regressions. The following is a detailed explanation of the test - * for your benefit if this test ever breaks or you are wondering what was the - * point of all this. You might want to skim the code below first. - * - * OS.File maintains a `queue` of operations to be performed. This queue is - * nominally implemented as a chain of promises. Every time a new job is - * OS.File.push()ed, it effectively becomes the new `queue` promise. (An - * extra promise is interposed with a rejection handler to avoid the rejection - * cascading, but that does not matter for our purposes.) - * - * The flaw in `kill` was that it would wait for the `queue` to complete before - * replacing `queue`. As a result, another OS.File operation could use `push` - * (by way of OS.File.post()) to also use .then() on the same `queue` promise. - * Accordingly, assuming that promise was not yet resolved (due to a pending - * OS.File request), when it was resolved, both the task scheduled in `kill` - * and in `post` would be triggered. Both of those tasks would run until - * encountering a call to worker.post(). - * - * Re-creating this race is not entirely trivial because of the large number of - * promises used by the code causing control flow to repeatedly be deferred. In - * a slightly simpler world we could run the follwing in the same turn of the - * event loop and trigger the problem. - * - any OS.File request - * - Scheduler.kill() - * - any OS.File request - * - * However, we need the Scheduler.kill task to reach the point where it is - * waiting on the same `queue` that another task has been scheduled against. - * Since the `kill` task yields on the `killQueue` promise prior to yielding - * on `queue`, however, some turns of the event loop are required. Happily, - * for us, as discussed above, the problem triggers when we have two promises - * scheduled on the `queue`, so we can just wait to schedule the second OS.File - * request on the queue. (Note that because of the additional then() added to - * eat rejections, there is an important difference between the value of - * `queue` and the value returned by the first OS.File request.) - */ -add_task(async function test_kill_race() { - // Ensure the worker has been created and that SET_DEBUG has taken effect. - // We have chosen OS.File.exists for our tests because it does not trigger - // a rejection and we absolutely do not care what the operation is other - // than it does not invoke a native fast-path. - await OS.File.exists("foo.foo"); - - info("issuing first request"); - let firstRequest = OS.File.exists("foo.bar"); // eslint-disable-line no-unused-vars - let secondRequest; - let secondResolved = false; - - // As noted in our big block comment, we want to wait to schedule the - // second request so that it races `kill`'s call to `worker.post`. Having - // ourselves wait on the same promise, `queue`, and registering ourselves - // before we issue the kill request means we will get run before the `kill` - // task resumes and allow us to precisely create the desired race. - Scheduler.queue.then(function() { - info("issuing second request"); - secondRequest = OS.File.exists("foo.baz"); - secondRequest.then(function() { - secondResolved = true; - }); - }); - - info("issuing kill request"); - let killRequest = Scheduler.kill({ reset: true, shutdown: false }); - - // Wait on the killRequest so that we can schedule a new OS.File request - // after it completes... - await killRequest; - // ...because our ordering guarantee ensures that there is at most one - // worker (and this usage here should not be vulnerable even with the - // bug present), so when this completes the secondRequest has either been - // resolved or lost. - await OS.File.exists("foo.goz"); - - ok( - secondResolved, - "The second request was resolved so we avoided the bug. Victory!" - ); -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_osfile_win_async_setPermissions.js +++ /dev/null @@ -1,135 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -/** - * A test to ensure that OS.File.setPermissions and - * OS.File.prototype.setPermissions are all working correctly. - * (see bug 1022816) - * The manifest tests on Windows. - */ - -// Sequence of setPermission parameters. -var testSequence = [ - [ - { winAttributes: { readOnly: true, system: true, hidden: true } }, - { readOnly: true, system: true, hidden: true }, - ], - [ - { winAttributes: { readOnly: false } }, - { readOnly: false, system: true, hidden: true }, - ], - [ - { winAttributes: { system: false } }, - { readOnly: false, system: false, hidden: true }, - ], - [ - { winAttributes: { hidden: false } }, - { readOnly: false, system: false, hidden: false }, - ], - [ - { winAttributes: { readOnly: true, system: false, hidden: false } }, - { readOnly: true, system: false, hidden: false }, - ], - [ - { winAttributes: { readOnly: false, system: true, hidden: false } }, - { readOnly: false, system: true, hidden: false }, - ], - [ - { winAttributes: { readOnly: false, system: false, hidden: true } }, - { readOnly: false, system: false, hidden: true }, - ], -]; - -// Test application to paths. -add_task(async function test_path_setPermissions() { - let path = OS.Path.join( - OS.Constants.Path.tmpDir, - "test_osfile_win_async_setPermissions_path.tmp" - ); - await OS.File.writeAtomic(path, new Uint8Array(1)); - - try { - for (let [options, attributesExpected] of testSequence) { - if (options !== null) { - info("Setting permissions to " + JSON.stringify(options)); - await OS.File.setPermissions(path, options); - } - - let stat = await OS.File.stat(path); - info("Got stat winAttributes: " + JSON.stringify(stat.winAttributes)); - - Assert.equal(stat.winAttributes.readOnly, attributesExpected.readOnly); - Assert.equal(stat.winAttributes.system, attributesExpected.system); - Assert.equal(stat.winAttributes.hidden, attributesExpected.hidden); - } - } finally { - await OS.File.remove(path); - } -}); - -// Test application to open files. -add_task(async function test_file_setPermissions() { - let path = OS.Path.join( - OS.Constants.Path.tmpDir, - "test_osfile_win_async_setPermissions_file.tmp" - ); - await OS.File.writeAtomic(path, new Uint8Array(1)); - - try { - let fd = await OS.File.open(path, { write: true }); - try { - for (let [options, attributesExpected] of testSequence) { - if (options !== null) { - info("Setting permissions to " + JSON.stringify(options)); - await fd.setPermissions(options); - } - - let stat = await fd.stat(); - info("Got stat winAttributes: " + JSON.stringify(stat.winAttributes)); - Assert.equal(stat.winAttributes.readOnly, attributesExpected.readOnly); - Assert.equal(stat.winAttributes.system, attributesExpected.system); - Assert.equal(stat.winAttributes.hidden, attributesExpected.hidden); - } - } finally { - await fd.close(); - } - } finally { - await OS.File.remove(path); - } -}); - -// Test application to Check setPermissions on a non-existant file path. -add_task(async function test_non_existant_file_path_setPermissions() { - let path = OS.Path.join( - OS.Constants.Path.tmpDir, - "test_osfile_win_async_setPermissions_path.tmp" - ); - await Assert.rejects( - OS.File.setPermissions(path, { winAttributes: { readOnly: true } }), - /The system cannot find the file specified/, - "setPermissions failed as expected on a non-existant file path" - ); -}); - -// Test application to Check setPermissions on a invalid file handle. -add_task(async function test_closed_file_handle_setPermissions() { - let path = OS.Path.join( - OS.Constants.Path.tmpDir, - "test_osfile_win_async_setPermissions_path.tmp" - ); - await OS.File.writeAtomic(path, new Uint8Array(1)); - - try { - let fd = await OS.File.open(path, { write: true }); - await fd.close(); - await Assert.rejects( - fd.setPermissions(path, { winAttributes: { readOnly: true } }), - /The handle is invalid/, - "setPermissions failed as expected on a invalid file handle" - ); - } finally { - await OS.File.remove(path); - } -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_osfile_writeAtomic_backupTo_option.js +++ /dev/null @@ -1,148 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -var { - OS: { File, Path, Constants }, -} = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -/** - * Remove all temporary files and back up files, including - * test_backupTo_option_with_tmpPath.tmp - * test_backupTo_option_with_tmpPath.tmp.backup - * test_backupTo_option_without_tmpPath.tmp - * test_backupTo_option_without_tmpPath.tmp.backup - * test_non_backupTo_option.tmp - * test_non_backupTo_option.tmp.backup - * test_backupTo_option_without_destination_file.tmp - * test_backupTo_option_without_destination_file.tmp.backup - * test_backupTo_option_with_backup_file.tmp - * test_backupTo_option_with_backup_file.tmp.backup - */ -async function clearFiles() { - let files = [ - "test_backupTo_option_with_tmpPath.tmp", - "test_backupTo_option_without_tmpPath.tmp", - "test_non_backupTo_option.tmp", - "test_backupTo_option_without_destination_file.tmp", - "test_backupTo_option_with_backup_file.tmp", - ]; - for (let file of files) { - let path = Path.join(Constants.Path.tmpDir, file); - await File.remove(path); - await File.remove(path + ".backup"); - } -} - -add_task(async function init() { - await clearFiles(); -}); - -/** - * test when - * |backupTo| specified - * |tmpPath| specified - * destination file exists - * @result destination file will be backed up - */ -add_task(async function test_backupTo_option_with_tmpPath() { - let DEFAULT_CONTENTS = "default contents" + Math.random(); - let WRITE_CONTENTS = "abc" + Math.random(); - let path = Path.join( - Constants.Path.tmpDir, - "test_backupTo_option_with_tmpPath.tmp" - ); - await File.writeAtomic(path, DEFAULT_CONTENTS); - await File.writeAtomic(path, WRITE_CONTENTS, { - tmpPath: path + ".tmp", - backupTo: path + ".backup", - }); - Assert.ok(await File.exists(path + ".backup")); - let contents = await File.read(path + ".backup"); - Assert.equal(DEFAULT_CONTENTS, new TextDecoder().decode(contents)); -}); - -/** - * test when - * |backupTo| specified - * |tmpPath| not specified - * destination file exists - * @result destination file will be backed up - */ -add_task(async function test_backupTo_option_without_tmpPath() { - let DEFAULT_CONTENTS = "default contents" + Math.random(); - let WRITE_CONTENTS = "abc" + Math.random(); - let path = Path.join( - Constants.Path.tmpDir, - "test_backupTo_option_without_tmpPath.tmp" - ); - await File.writeAtomic(path, DEFAULT_CONTENTS); - await File.writeAtomic(path, WRITE_CONTENTS, { backupTo: path + ".backup" }); - Assert.ok(await File.exists(path + ".backup")); - let contents = await File.read(path + ".backup"); - Assert.equal(DEFAULT_CONTENTS, new TextDecoder().decode(contents)); -}); - -/** - * test when - * |backupTo| not specified - * |tmpPath| not specified - * destination file exists - * @result destination file will not be backed up - */ -add_task(async function test_non_backupTo_option() { - let DEFAULT_CONTENTS = "default contents" + Math.random(); - let WRITE_CONTENTS = "abc" + Math.random(); - let path = Path.join(Constants.Path.tmpDir, "test_non_backupTo_option.tmp"); - await File.writeAtomic(path, DEFAULT_CONTENTS); - await File.writeAtomic(path, WRITE_CONTENTS); - Assert.equal(false, await File.exists(path + ".backup")); -}); - -/** - * test when - * |backupTo| specified - * |tmpPath| not specified - * destination file not exists - * @result no back up file exists - */ -add_task(async function test_backupTo_option_without_destination_file() { - let WRITE_CONTENTS = "abc" + Math.random(); - let path = Path.join( - Constants.Path.tmpDir, - "test_backupTo_option_without_destination_file.tmp" - ); - await File.remove(path); - await File.writeAtomic(path, WRITE_CONTENTS, { backupTo: path + ".backup" }); - Assert.equal(false, await File.exists(path + ".backup")); -}); - -/** - * test when - * |backupTo| specified - * |tmpPath| not specified - * backup file exists - * destination file exists - * @result destination file will be backed up - */ -add_task(async function test_backupTo_option_with_backup_file() { - let DEFAULT_CONTENTS = "default contents" + Math.random(); - let WRITE_CONTENTS = "abc" + Math.random(); - let path = Path.join( - Constants.Path.tmpDir, - "test_backupTo_option_with_backup_file.tmp" - ); - await File.writeAtomic(path, DEFAULT_CONTENTS); - - await File.writeAtomic(path + ".backup", new Uint8Array(1000)); - - await File.writeAtomic(path, WRITE_CONTENTS, { backupTo: path + ".backup" }); - Assert.ok(await File.exists(path + ".backup")); - let contents = await File.read(path + ".backup"); - Assert.equal(DEFAULT_CONTENTS, new TextDecoder().decode(contents)); -}); - -add_task(async function cleanup() { - await clearFiles(); -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_osfile_writeAtomic_unicode_filename.js +++ /dev/null @@ -1,48 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * This test checks against failures that may occur while creating and/or - * renaming files with Unicode paths on Windows. - * See bug 1063635#c89 for a failure due to a Unicode filename being renamed. - */ - -"use strict"; -var profileDir; - -async function writeAndCheck(path, tmpPath) { - const encoder = new TextEncoder(); - const content = "tmpContent"; - const outBin = encoder.encode(content); - await OS.File.writeAtomic(path, outBin, { tmpPath }); - - const decoder = new TextDecoder(); - const writtenBin = await OS.File.read(path); - const written = decoder.decode(writtenBin); - - // Clean up - await OS.File.remove(path); - Assert.equal( - written, - content, - `Expected correct write/read for ${path} with tmpPath ${tmpPath}` - ); -} - -add_task(async function init() { - do_get_profile(); - profileDir = OS.Constants.Path.profileDir; -}); - -add_test_pair(async function test_osfile_writeAtomic_unicode_filename() { - await writeAndCheck(OS.Path.join(profileDir, "☕") + ".tmp", undefined); - await writeAndCheck(OS.Path.join(profileDir, "☕"), undefined); - await writeAndCheck( - OS.Path.join(profileDir, "☕") + ".tmp", - OS.Path.join(profileDir, "☕") - ); - await writeAndCheck( - OS.Path.join(profileDir, "☕"), - OS.Path.join(profileDir, "☕") + ".tmp" - ); -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_osfile_writeAtomic_zerobytes.js +++ /dev/null @@ -1,26 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; -var SHARED_PATH; - -add_task(async function init() { - do_get_profile(); - SHARED_PATH = OS.Path.join( - OS.Constants.Path.profileDir, - "test_osfile_write_zerobytes.tmp" - ); -}); - -add_test_pair(async function test_osfile_writeAtomic_zerobytes() { - let encoder = new TextEncoder(); - let string1 = ""; - let outbin = encoder.encode(string1); - await OS.File.writeAtomic(SHARED_PATH, outbin); - - let decoder = new TextDecoder(); - let bin = await OS.File.read(SHARED_PATH); - let string2 = decoder.decode(bin); - // Checking if writeAtomic supports writing encoded zero-byte strings - Assert.equal(string2, string1, "Read the expected (empty) string."); -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_path.js +++ /dev/null @@ -1,187 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -Services.prefs.setBoolPref("toolkit.osfile.test.syslib_necessary", false); -// We don't need libc/kernel32.dll for this test - -const Win = ChromeUtils.import("resource://gre/modules/osfile/ospath_win.jsm"); -const Unix = ChromeUtils.import( - "resource://gre/modules/osfile/ospath_unix.jsm" -); - -function do_check_fail(f) { - try { - let result = f(); - info("Failed do_check_fail: " + result); - Assert.ok(false); - } catch (ex) { - Assert.ok(true); - } -} - -function run_test() { - info("Testing Windows paths"); - - info("Backslash-separated, no drive"); - Assert.equal(Win.basename("a\\b"), "b"); - Assert.equal(Win.basename("a\\b\\"), ""); - Assert.equal(Win.basename("abc"), "abc"); - Assert.equal(Win.dirname("a\\b"), "a"); - Assert.equal(Win.dirname("a\\b\\"), "a\\b"); - Assert.equal(Win.dirname("a\\\\\\\\b"), "a"); - Assert.equal(Win.dirname("abc"), "."); - Assert.equal(Win.normalize("\\a\\b\\c"), "\\a\\b\\c"); - Assert.equal(Win.normalize("\\a\\b\\\\\\\\c"), "\\a\\b\\c"); - Assert.equal(Win.normalize("\\a\\b\\c\\\\\\"), "\\a\\b\\c"); - Assert.equal(Win.normalize("\\a\\b\\c\\..\\..\\..\\d\\e\\f"), "\\d\\e\\f"); - Assert.equal(Win.normalize("a\\b\\c\\..\\..\\..\\d\\e\\f"), "d\\e\\f"); - do_check_fail(() => Win.normalize("\\a\\b\\c\\..\\..\\..\\..\\d\\e\\f")); - - Assert.equal( - Win.join("\\tmp", "foo", "bar"), - "\\tmp\\foo\\bar", - "join \\tmp,foo,bar" - ); - Assert.equal( - Win.join("\\tmp", "\\foo", "bar"), - "\\foo\\bar", - "join \\tmp,\\foo,bar" - ); - Assert.equal(Win.winGetDrive("\\tmp"), null); - Assert.equal(Win.winGetDrive("\\tmp\\a\\b\\c\\d\\e"), null); - Assert.equal(Win.winGetDrive("\\"), null); - - info("Backslash-separated, with a drive"); - Assert.equal(Win.basename("c:a\\b"), "b"); - Assert.equal(Win.basename("c:a\\b\\"), ""); - Assert.equal(Win.basename("c:abc"), "abc"); - Assert.equal(Win.dirname("c:a\\b"), "c:a"); - Assert.equal(Win.dirname("c:a\\b\\"), "c:a\\b"); - Assert.equal(Win.dirname("c:a\\\\\\\\b"), "c:a"); - Assert.equal(Win.dirname("c:abc"), "c:"); - let options = { - winNoDrive: true, - }; - Assert.equal(Win.dirname("c:a\\b", options), "a"); - Assert.equal(Win.dirname("c:a\\b\\", options), "a\\b"); - Assert.equal(Win.dirname("c:a\\\\\\\\b", options), "a"); - Assert.equal(Win.dirname("c:abc", options), "."); - Assert.equal(Win.join("c:", "abc"), "c:\\abc", "join c:,abc"); - - Assert.equal(Win.normalize("c:"), "c:\\"); - Assert.equal(Win.normalize("c:\\"), "c:\\"); - Assert.equal(Win.normalize("c:\\a\\b\\c"), "c:\\a\\b\\c"); - Assert.equal(Win.normalize("c:\\a\\b\\\\\\\\c"), "c:\\a\\b\\c"); - Assert.equal(Win.normalize("c:\\\\\\\\a\\b\\c"), "c:\\a\\b\\c"); - Assert.equal(Win.normalize("c:\\a\\b\\c\\\\\\"), "c:\\a\\b\\c"); - Assert.equal( - Win.normalize("c:\\a\\b\\c\\..\\..\\..\\d\\e\\f"), - "c:\\d\\e\\f" - ); - Assert.equal(Win.normalize("c:a\\b\\c\\..\\..\\..\\d\\e\\f"), "c:\\d\\e\\f"); - do_check_fail(() => Win.normalize("c:\\a\\b\\c\\..\\..\\..\\..\\d\\e\\f")); - - Assert.equal(Win.join("c:\\", "foo"), "c:\\foo", "join c:,foo"); - Assert.equal( - Win.join("c:\\tmp", "foo", "bar"), - "c:\\tmp\\foo\\bar", - "join c:\\tmp,foo,bar" - ); - Assert.equal( - Win.join("c:\\tmp", "\\foo", "bar"), - "c:\\foo\\bar", - "join c:\\tmp,\\foo,bar" - ); - Assert.equal( - Win.join("c:\\tmp", "c:\\foo", "bar"), - "c:\\foo\\bar", - "join c:\\tmp,c:\\foo,bar" - ); - Assert.equal( - Win.join("c:\\tmp", "c:foo", "bar"), - "c:\\foo\\bar", - "join c:\\tmp,c:foo,bar" - ); - Assert.equal(Win.winGetDrive("c:"), "c:"); - Assert.equal(Win.winGetDrive("c:\\"), "c:"); - Assert.equal(Win.winGetDrive("c:abc"), "c:"); - Assert.equal(Win.winGetDrive("c:abc\\d\\e\\f\\g"), "c:"); - Assert.equal(Win.winGetDrive("c:\\abc"), "c:"); - Assert.equal(Win.winGetDrive("c:\\abc\\d\\e\\f\\g"), "c:"); - - info("Forwardslash-separated, no drive"); - Assert.equal(Win.normalize("/a/b/c"), "\\a\\b\\c"); - Assert.equal(Win.normalize("/a/b////c"), "\\a\\b\\c"); - Assert.equal(Win.normalize("/a/b/c///"), "\\a\\b\\c"); - Assert.equal(Win.normalize("/a/b/c/../../../d/e/f"), "\\d\\e\\f"); - Assert.equal(Win.normalize("a/b/c/../../../d/e/f"), "d\\e\\f"); - - info("Forwardslash-separated, with a drive"); - Assert.equal(Win.normalize("c:/"), "c:\\"); - Assert.equal(Win.normalize("c:/a/b/c"), "c:\\a\\b\\c"); - Assert.equal(Win.normalize("c:/a/b////c"), "c:\\a\\b\\c"); - Assert.equal(Win.normalize("c:////a/b/c"), "c:\\a\\b\\c"); - Assert.equal(Win.normalize("c:/a/b/c///"), "c:\\a\\b\\c"); - Assert.equal(Win.normalize("c:/a/b/c/../../../d/e/f"), "c:\\d\\e\\f"); - Assert.equal(Win.normalize("c:a/b/c/../../../d/e/f"), "c:\\d\\e\\f"); - - info("Backslash-separated, UNC-style"); - Assert.equal(Win.basename("\\\\a\\b"), "b"); - Assert.equal(Win.basename("\\\\a\\b\\"), ""); - Assert.equal(Win.basename("\\\\abc"), ""); - Assert.equal(Win.dirname("\\\\a\\b"), "\\\\a"); - Assert.equal(Win.dirname("\\\\a\\b\\"), "\\\\a\\b"); - Assert.equal(Win.dirname("\\\\a\\\\\\\\b"), "\\\\a"); - Assert.equal(Win.dirname("\\\\abc"), "\\\\abc"); - Assert.equal(Win.normalize("\\\\a\\b\\c"), "\\\\a\\b\\c"); - Assert.equal(Win.normalize("\\\\a\\b\\\\\\\\c"), "\\\\a\\b\\c"); - Assert.equal(Win.normalize("\\\\a\\b\\c\\\\\\"), "\\\\a\\b\\c"); - Assert.equal(Win.normalize("\\\\a\\b\\c\\..\\..\\d\\e\\f"), "\\\\a\\d\\e\\f"); - do_check_fail(() => Win.normalize("\\\\a\\b\\c\\..\\..\\..\\d\\e\\f")); - - Assert.equal(Win.join("\\\\a\\tmp", "foo", "bar"), "\\\\a\\tmp\\foo\\bar"); - Assert.equal(Win.join("\\\\a\\tmp", "\\foo", "bar"), "\\\\a\\foo\\bar"); - Assert.equal(Win.join("\\\\a\\tmp", "\\\\foo\\", "bar"), "\\\\foo\\bar"); - Assert.equal(Win.winGetDrive("\\\\"), null); - Assert.equal(Win.winGetDrive("\\\\c"), "\\\\c"); - Assert.equal(Win.winGetDrive("\\\\c\\abc"), "\\\\c"); - - info("Testing unix paths"); - Assert.equal(Unix.basename("a/b"), "b"); - Assert.equal(Unix.basename("a/b/"), ""); - Assert.equal(Unix.basename("abc"), "abc"); - Assert.equal(Unix.dirname("a/b"), "a"); - Assert.equal(Unix.dirname("a/b/"), "a/b"); - Assert.equal(Unix.dirname("a////b"), "a"); - Assert.equal(Unix.dirname("abc"), "."); - Assert.equal(Unix.normalize("/a/b/c"), "/a/b/c"); - Assert.equal(Unix.normalize("/a/b////c"), "/a/b/c"); - Assert.equal(Unix.normalize("////a/b/c"), "/a/b/c"); - Assert.equal(Unix.normalize("/a/b/c///"), "/a/b/c"); - Assert.equal(Unix.normalize("/a/b/c/../../../d/e/f"), "/d/e/f"); - Assert.equal(Unix.normalize("a/b/c/../../../d/e/f"), "d/e/f"); - do_check_fail(() => Unix.normalize("/a/b/c/../../../../d/e/f")); - - Assert.equal( - Unix.join("/tmp", "foo", "bar"), - "/tmp/foo/bar", - "join /tmp,foo,bar" - ); - Assert.equal( - Unix.join("/tmp", "/foo", "bar"), - "/foo/bar", - "join /tmp,/foo,bar" - ); - - info("Testing the presence of ospath.jsm"); - let scope; - try { - scope = ChromeUtils.import("resource://gre/modules/osfile/ospath.jsm"); - } catch (ex) { - // Can't load ospath - } - Assert.ok(!!scope.basename); -}
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_path_constants.js +++ /dev/null @@ -1,83 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const { AppConstants } = ChromeUtils.importESModule( - "resource://gre/modules/AppConstants.sys.mjs" -); -const { ctypes } = ChromeUtils.importESModule( - "resource://gre/modules/ctypes.sys.mjs" -); -const { makeFakeAppDir } = ChromeUtils.importESModule( - "resource://testing-common/AppData.sys.mjs" -); - -function compare_paths(ospath, key) { - let file; - try { - file = Services.dirsvc.get(key, Ci.nsIFile); - } catch (ex) {} - - if (file) { - Assert.ok(!!ospath); - Assert.equal(ospath, file.path); - } else { - info( - "WARNING: " + key + " is not defined. Test may not be testing anything!" - ); - Assert.ok(!ospath); - } -} - -// Test simple paths -add_task(async function test_simple_paths() { - Assert.ok(!!OS.Constants.Path.tmpDir); - compare_paths(OS.Constants.Path.tmpDir, "TmpD"); -}); - -// Some path constants aren't set up until the profile is available. This -// test verifies that behavior. -add_task(async function test_before_after_profile() { - // On Android the profile is initialized during xpcshell init, so this test - // will fail. - if (AppConstants.platform != "android") { - Assert.equal(null, OS.Constants.Path.profileDir); - Assert.equal(null, OS.Constants.Path.localProfileDir); - Assert.equal(null, OS.Constants.Path.userApplicationDataDir); - } - - do_get_profile(); - Assert.ok(!!OS.Constants.Path.profileDir); - Assert.ok(!!OS.Constants.Path.localProfileDir); - - // UAppData is still null because the xpcshell profile doesn't set it up. - // This test is mostly here to fail in case behavior of do_get_profile() ever - // changes. We want to know if our assumptions no longer hold! - Assert.equal(null, OS.Constants.Path.userApplicationDataDir); - - await makeFakeAppDir(); - Assert.ok(!!OS.Constants.Path.userApplicationDataDir); - - // FUTURE: verify AppData too (bug 964291). -}); - -// Test presence of paths that only exist on Desktop platforms -add_task(async function test_desktop_paths() { - if (OS.Constants.Sys.Name == "Android") { - return; - } - Assert.ok(!!OS.Constants.Path.homeDir); - - compare_paths(OS.Constants.Path.homeDir, "Home"); - compare_paths(OS.Constants.Path.userApplicationDataDir, "UAppData"); - - compare_paths(OS.Constants.Path.macUserLibDir, "ULibDir"); -}); - -// Open libxul -add_task(async function test_libxul() { - ctypes.open(OS.Constants.Path.libxul); - info("Linked to libxul"); -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_queue.js +++ /dev/null @@ -1,34 +0,0 @@ -"use strict"; - -const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -// Check if Scheduler.queue returned by OS.File.queue is resolved initially. -add_task(async function check_init() { - await OS.File.queue; - info("Function resolved"); -}); - -// Check if Scheduler.queue returned by OS.File.queue is resolved -// after an operation is successful. -add_task(async function check_success() { - info("Attempting to open a file correctly"); - await OS.File.open(OS.Path.join(do_get_cwd().path, "test_queue.js")); - info("File opened correctly"); - await OS.File.queue; - info("Function resolved"); -}); - -// Check if Scheduler.queue returned by OS.File.queue is resolved -// after an operation fails. -add_task(async function check_failure() { - let exception; - try { - info("Attempting to open a non existing file"); - await OS.File.open(OS.Path.join(".", "Bigfoot")); - } catch (err) { - exception = err; - await OS.File.queue; - } - Assert.ok(exception != null); - info("Function resolved"); -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_read_write.js +++ /dev/null @@ -1,119 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -var SHARED_PATH; - -var EXISTING_FILE = do_get_file("xpcshell.ini").path; - -add_task(async function init() { - do_get_profile(); - SHARED_PATH = OS.Path.join( - OS.Constants.Path.profileDir, - "test_osfile_read.tmp" - ); -}); - -// Check that OS.File.read() is executed after the previous operation -add_test_pair(async function ordering() { - let string1 = "Initial state " + Math.random(); - let string2 = "After writing " + Math.random(); - await OS.File.writeAtomic(SHARED_PATH, string1); - OS.File.writeAtomic(SHARED_PATH, string2); - let string3 = await OS.File.read(SHARED_PATH, { encoding: "utf-8" }); - Assert.equal(string3, string2); -}); - -add_test_pair(async function read_write_all() { - let DEST_PATH = SHARED_PATH + Math.random(); - let TMP_PATH = DEST_PATH + ".tmp"; - - let test_with_options = function(options, suffix) { - return (async function() { - info( - "Running test read_write_all with options " + JSON.stringify(options) - ); - let TEST = "read_write_all " + suffix; - - let optionsBackup = JSON.parse(JSON.stringify(options)); - - // Check that read + writeAtomic performs a correct copy - let currentDir = await OS.File.getCurrentDirectory(); - let pathSource = OS.Path.join(currentDir, EXISTING_FILE); - let contents = await OS.File.read(pathSource); - Assert.ok(!!contents); // Content is not empty - let bytesRead = contents.byteLength; - - let bytesWritten = await OS.File.writeAtomic( - DEST_PATH, - contents, - options - ); - Assert.equal(bytesRead, bytesWritten); // Correct number of bytes written - - // Check that options are not altered - Assert.equal(JSON.stringify(options), JSON.stringify(optionsBackup)); - await reference_compare_files(pathSource, DEST_PATH, TEST); - - // Check that temporary file was removed or never created exist - Assert.ok(!new FileUtils.File(TMP_PATH).exists()); - - // Check that writeAtomic fails if noOverwrite is true and the destination - // file already exists! - contents = new Uint8Array(300); - let view = new Uint8Array(contents.buffer, 10, 200); - try { - let opt = JSON.parse(JSON.stringify(options)); - opt.noOverwrite = true; - await OS.File.writeAtomic(DEST_PATH, view, opt); - do_throw( - "With noOverwrite, writeAtomic should have refused to overwrite file (" + - suffix + - ")" - ); - } catch (err) { - if (err instanceof OS.File.Error && err.becauseExists) { - info( - "With noOverwrite, writeAtomic correctly failed (" + suffix + ")" - ); - } else { - throw err; - } - } - await reference_compare_files(pathSource, DEST_PATH, TEST); - - // Check that temporary file was removed or never created - Assert.ok(!new FileUtils.File(TMP_PATH).exists()); - - // Now write a subset - let START = 10; - let LENGTH = 100; - contents = new Uint8Array(300); - for (let i = 0; i < contents.byteLength; i++) { - contents[i] = i % 256; - } - view = new Uint8Array(contents.buffer, START, LENGTH); - bytesWritten = await OS.File.writeAtomic(DEST_PATH, view, options); - Assert.equal(bytesWritten, LENGTH); - - let array2 = await OS.File.read(DEST_PATH); - Assert.equal(LENGTH, array2.length); - for (let j = 0; j < LENGTH; j++) { - Assert.equal(array2[j], (j + START) % 256); - } - - // Cleanup. - await OS.File.remove(DEST_PATH); - await OS.File.remove(TMP_PATH); - })(); - }; - - await test_with_options({ tmpPath: TMP_PATH }, "Renaming, not flushing"); - await test_with_options( - { tmpPath: TMP_PATH, flush: true }, - "Renaming, flushing" - ); - await test_with_options({}, "Not renaming, not flushing"); - await test_with_options({ flush: true }, "Not renaming, flushing"); -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_remove.js +++ /dev/null @@ -1,60 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -registerCleanupFunction(function() { - Services.prefs.setBoolPref("toolkit.osfile.log", false); -}); - -function run_test() { - Services.prefs.setBoolPref("toolkit.osfile.log", true); - run_next_test(); -} - -add_task(async function test_ignoreAbsent() { - let absent_file_name = "test_osfile_front_absent.tmp"; - - // Removing absent files should throw if "ignoreAbsent" is true. - await Assert.rejects( - OS.File.remove(absent_file_name, { ignoreAbsent: false }), - err => err.operation == "remove", - "OS.File.remove throws if there is no such file." - ); - - // Removing absent files should not throw if "ignoreAbsent" is true or not - // defined. - let exception = null; - try { - await OS.File.remove(absent_file_name, { ignoreAbsent: true }); - await OS.File.remove(absent_file_name); - } catch (ex) { - exception = ex; - } - Assert.ok(!exception, "OS.File.remove should not throw when not requested."); -}); - -add_task(async function test_ignoreAbsent_directory_missing() { - let absent_file_name = OS.Path.join("absent_parent", "test.tmp"); - - // Removing absent files should throw if "ignoreAbsent" is true. - await Assert.rejects( - OS.File.remove(absent_file_name, { ignoreAbsent: false }), - err => err.operation == "remove", - "OS.File.remove throws if there is no such file." - ); - - // Removing files from absent directories should not throw if "ignoreAbsent" - // is true or not defined. - let exception = null; - try { - await OS.File.remove(absent_file_name, { ignoreAbsent: true }); - await OS.File.remove(absent_file_name); - } catch (ex) { - exception = ex; - } - Assert.ok(!exception, "OS.File.remove should not throw when not requested."); -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_removeDir.js +++ /dev/null @@ -1,177 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -registerCleanupFunction(function() { - Services.prefs.setBoolPref("toolkit.osfile.log", false); -}); - -function run_test() { - Services.prefs.setBoolPref("toolkit.osfile.log", true); - - run_next_test(); -} - -add_task(async function() { - // Set up profile. We create the directory in the profile, because the profile - // is removed after every test run. - do_get_profile(); - - let file = OS.Path.join(OS.Constants.Path.profileDir, "file"); - let dir = OS.Path.join(OS.Constants.Path.profileDir, "directory"); - let file1 = OS.Path.join(dir, "file1"); - let file2 = OS.Path.join(dir, "file2"); - let subDir = OS.Path.join(dir, "subdir"); - let fileInSubDir = OS.Path.join(subDir, "file"); - - // Sanity checking for the test - Assert.equal(false, await OS.File.exists(dir)); - - // Remove non-existent directory - let exception = null; - try { - await OS.File.removeDir(dir, { ignoreAbsent: false }); - } catch (ex) { - exception = ex; - } - - Assert.ok(!!exception); - Assert.ok(exception instanceof OS.File.Error); - - // Remove non-existent directory with ignoreAbsent - await OS.File.removeDir(dir, { ignoreAbsent: true }); - await OS.File.removeDir(dir); - - // Remove file with ignoreAbsent: false - await OS.File.writeAtomic(file, "content", { tmpPath: file + ".tmp" }); - exception = null; - try { - await OS.File.removeDir(file, { ignoreAbsent: false }); - } catch (ex) { - exception = ex; - } - - Assert.ok(!!exception); - Assert.ok(exception instanceof OS.File.Error); - - // Remove empty directory - await OS.File.makeDir(dir); - await OS.File.removeDir(dir); - Assert.equal(false, await OS.File.exists(dir)); - - // Remove directory that contains one file - await OS.File.makeDir(dir); - await OS.File.writeAtomic(file1, "content", { tmpPath: file1 + ".tmp" }); - await OS.File.removeDir(dir); - Assert.equal(false, await OS.File.exists(dir)); - - // Remove directory that contains multiple files - await OS.File.makeDir(dir); - await OS.File.writeAtomic(file1, "content", { tmpPath: file1 + ".tmp" }); - await OS.File.writeAtomic(file2, "content", { tmpPath: file2 + ".tmp" }); - await OS.File.removeDir(dir); - Assert.equal(false, await OS.File.exists(dir)); - - // Remove directory that contains a file and a directory - await OS.File.makeDir(dir); - await OS.File.writeAtomic(file1, "content", { tmpPath: file1 + ".tmp" }); - await OS.File.makeDir(subDir); - await OS.File.writeAtomic(fileInSubDir, "content", { - tmpPath: fileInSubDir + ".tmp", - }); - await OS.File.removeDir(dir); - Assert.equal(false, await OS.File.exists(dir)); -}); - -add_task(async function test_unix_symlink() { - // Windows does not implement OS.File.unixSymLink() - if (OS.Constants.Win) { - return; - } - - // Android / B2G file systems typically don't support symlinks. - if (OS.Constants.Sys.Name == "Android") { - return; - } - - let file = OS.Path.join(OS.Constants.Path.profileDir, "file"); - let dir = OS.Path.join(OS.Constants.Path.profileDir, "directory"); - let file1 = OS.Path.join(dir, "file1"); - - // This test will create the following directory structure: - // <profileDir>/file (regular file) - // <profileDir>/file.link => file (symlink) - // <profileDir>/directory (directory) - // <profileDir>/linkdir => directory (directory) - // <profileDir>/directory/file1 (regular file) - // <profileDir>/directory3 (directory) - // <profileDir>/directory3/file3 (directory) - // <profileDir>/directory/link2 => ../directory3 (regular file) - - // Sanity checking for the test - Assert.equal(false, await OS.File.exists(dir)); - - await OS.File.writeAtomic(file, "content", { tmpPath: file + ".tmp" }); - Assert.ok(await OS.File.exists(file)); - let info = await OS.File.stat(file, { unixNoFollowingLinks: true }); - Assert.ok(!info.isDir); - Assert.ok(!info.isSymLink); - - await OS.File.unixSymLink(file, file + ".link"); - Assert.ok(await OS.File.exists(file + ".link")); - info = await OS.File.stat(file + ".link", { unixNoFollowingLinks: true }); - Assert.ok(!info.isDir); - Assert.ok(info.isSymLink); - info = await OS.File.stat(file + ".link"); - Assert.ok(!info.isDir); - Assert.ok(!info.isSymLink); - await OS.File.remove(file + ".link"); - Assert.equal(false, await OS.File.exists(file + ".link")); - - await OS.File.makeDir(dir); - Assert.ok(await OS.File.exists(dir)); - info = await OS.File.stat(dir, { unixNoFollowingLinks: true }); - Assert.ok(info.isDir); - Assert.ok(!info.isSymLink); - - let link = OS.Path.join(OS.Constants.Path.profileDir, "linkdir"); - - await OS.File.unixSymLink(dir, link); - Assert.ok(await OS.File.exists(link)); - info = await OS.File.stat(link, { unixNoFollowingLinks: true }); - Assert.ok(!info.isDir); - Assert.ok(info.isSymLink); - info = await OS.File.stat(link); - Assert.ok(info.isDir); - Assert.ok(!info.isSymLink); - - let dir3 = OS.Path.join(OS.Constants.Path.profileDir, "directory3"); - let file3 = OS.Path.join(dir3, "file3"); - let link2 = OS.Path.join(dir, "link2"); - - await OS.File.writeAtomic(file1, "content", { tmpPath: file1 + ".tmp" }); - Assert.ok(await OS.File.exists(file1)); - await OS.File.makeDir(dir3); - Assert.ok(await OS.File.exists(dir3)); - await OS.File.writeAtomic(file3, "content", { tmpPath: file3 + ".tmp" }); - Assert.ok(await OS.File.exists(file3)); - await OS.File.unixSymLink("../directory3", link2); - Assert.ok(await OS.File.exists(link2)); - - await OS.File.removeDir(link); - Assert.equal(false, await OS.File.exists(link)); - Assert.ok(await OS.File.exists(file1)); - await OS.File.removeDir(dir); - Assert.equal(false, await OS.File.exists(dir)); - Assert.ok(await OS.File.exists(file3)); - await OS.File.removeDir(dir3); - Assert.equal(false, await OS.File.exists(dir3)); - - // This task will be executed only on Unix-like systems. - // Please do not add tests independent to operating systems here - // or implement symlink() on Windows. -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_removeEmptyDir.js +++ /dev/null @@ -1,54 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -registerCleanupFunction(function() { - Services.prefs.setBoolPref("toolkit.osfile.log", false); -}); - -function run_test() { - Services.prefs.setBoolPref("toolkit.osfile.log", true); - - run_next_test(); -} - -/** - * Test OS.File.removeEmptyDir - */ -add_task(async function() { - // Set up profile. We create the directory in the profile, because the profile - // is removed after every test run. - do_get_profile(); - - let dir = OS.Path.join(OS.Constants.Path.profileDir, "directory"); - - // Sanity checking for the test - Assert.equal(false, await OS.File.exists(dir)); - - // Remove non-existent directory - await OS.File.removeEmptyDir(dir); - - // Remove non-existent directory with ignoreAbsent - await OS.File.removeEmptyDir(dir, { ignoreAbsent: true }); - - // Remove non-existent directory with ignoreAbsent false - let exception = null; - try { - await OS.File.removeEmptyDir(dir, { ignoreAbsent: false }); - } catch (ex) { - exception = ex; - } - - Assert.ok(!!exception); - Assert.ok(exception instanceof OS.File.Error); - Assert.ok(exception.becauseNoSuchFile); - - // Remove empty directory - await OS.File.makeDir(dir); - await OS.File.removeEmptyDir(dir); - Assert.equal(false, await OS.File.exists(dir)); -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_reset.js +++ /dev/null @@ -1,102 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -var Path = OS.Constants.Path; - -add_task(async function init() { - do_get_profile(); -}); - -add_task(async function reset_before_launching() { - info("Reset without launching OS.File, it shouldn't break"); - await OS.File.resetWorker(); -}); - -add_task(async function transparent_reset() { - for (let i = 1; i < 3; ++i) { - info( - "Do stome stuff before and after " + - i + - " reset(s), " + - "it shouldn't break" - ); - let CONTENT = "some content " + i; - let path = OS.Path.join(Path.profileDir, "tmp"); - await OS.File.writeAtomic(path, CONTENT); - for (let j = 0; j < i; ++j) { - await OS.File.resetWorker(); - } - let data = await OS.File.read(path); - let string = new TextDecoder().decode(data); - Assert.equal(string, CONTENT); - } -}); - -add_task(async function file_open_cannot_reset() { - let TEST_FILE = OS.Path.join(Path.profileDir, "tmp-" + Math.random()); - info( - "Leaking file descriptor " + TEST_FILE + ", we shouldn't be able to reset" - ); - let openedFile = await OS.File.open(TEST_FILE, { create: true }); - let thrown = false; - try { - await OS.File.resetWorker(); - } catch (ex) { - if (ex.message.includes(OS.Path.basename(TEST_FILE))) { - thrown = true; - } else { - throw ex; - } - } - Assert.ok(thrown); - - info("Closing the file, we should now be able to reset"); - await openedFile.close(); - await OS.File.resetWorker(); -}); - -add_task(async function dir_open_cannot_reset() { - let TEST_DIR = await OS.File.getCurrentDirectory(); - info("Leaking directory " + TEST_DIR + ", we shouldn't be able to reset"); - let iterator = new OS.File.DirectoryIterator(TEST_DIR); - let thrown = false; - try { - await OS.File.resetWorker(); - } catch (ex) { - if (ex.message.includes(OS.Path.basename(TEST_DIR))) { - thrown = true; - } else { - throw ex; - } - } - Assert.ok(thrown); - - info("Closing the directory, we should now be able to reset"); - await iterator.close(); - await OS.File.resetWorker(); -}); - -add_task(async function race_against_itself() { - info("Attempt to get resetWorker() to race against itself"); - // Arbitrary operation, just to wake up the worker - try { - await OS.File.read("/foo"); - } catch (ex) {} - - let all = []; - for (let i = 0; i < 100; ++i) { - all.push(OS.File.resetWorker()); - } - - await Promise.all(all); -}); - -add_task(async function finish_with_a_reset() { - info("Reset without waiting for the result"); - // Arbitrary operation, just to wake up the worker - try { - await OS.File.read("/foo"); - } catch (ex) {} - // Now reset - /* don't yield*/ OS.File.resetWorker(); -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_shutdown.js +++ /dev/null @@ -1,103 +0,0 @@ -const { PromiseUtils } = ChromeUtils.importESModule( - "resource://gre/modules/PromiseUtils.sys.mjs" -); - -add_task(function init() { - do_get_profile(); -}); - -/** - * Test logging of file descriptors leaks. - */ -add_task(async function system_shutdown() { - // Test that unclosed files cause warnings - // Test that unclosed directories cause warnings - // Test that closed files do not cause warnings - // Test that closed directories do not cause warnings - function testLeaksOf(resource, topic) { - return (async function() { - let deferred = PromiseUtils.defer(); - - // Register observer - Services.prefs.setBoolPref("toolkit.asyncshutdown.testing", true); - Services.prefs.setBoolPref("toolkit.osfile.log", true); - Services.prefs.setBoolPref("toolkit.osfile.log.redirect", true); - Services.prefs.setCharPref( - "toolkit.osfile.test.shutdown.observer", - topic - ); - - let observer = function(aMessage) { - try { - info("Got message: " + aMessage); - if (!(aMessage instanceof Ci.nsIConsoleMessage)) { - return; - } - let message = aMessage.message; - info("Got message: " + message); - if (!message.includes("TEST OS Controller WARNING")) { - return; - } - info( - "Got message: " + message + ", looking for resource " + resource - ); - if (!message.includes(resource)) { - return; - } - info("Resource: " + resource + " found"); - executeSoon(deferred.resolve); - } catch (ex) { - executeSoon(function() { - deferred.reject(ex); - }); - } - }; - Services.console.registerListener(observer); - Services.obs.notifyObservers(null, topic); - do_timeout(1000, function() { - info("Timeout while waiting for resource: " + resource); - deferred.reject("timeout"); - }); - - let resolved = false; - try { - await deferred.promise; - resolved = true; - } catch (ex) { - if (ex == "timeout") { - resolved = false; - } else { - throw ex; - } - } - Services.console.unregisterListener(observer); - Services.prefs.clearUserPref("toolkit.osfile.log"); - Services.prefs.clearUserPref("toolkit.osfile.log.redirect"); - Services.prefs.clearUserPref("toolkit.osfile.test.shutdown.observer"); - Services.prefs.clearUserPref("toolkit.async_shutdown.testing"); - - return resolved; - })(); - } - - let TEST_DIR = OS.Path.join(await OS.File.getCurrentDirectory(), ".."); - info("Testing for leaks of directory iterator " + TEST_DIR); - let iterator = new OS.File.DirectoryIterator(TEST_DIR); - info("At this stage, we leak the directory"); - Assert.ok(await testLeaksOf(TEST_DIR, "test.shutdown.dir.leak")); - await iterator.close(); - info("At this stage, we don't leak the directory anymore"); - Assert.equal(false, await testLeaksOf(TEST_DIR, "test.shutdown.dir.noleak")); - - let TEST_FILE = OS.Path.join(OS.Constants.Path.profileDir, "test"); - info("Testing for leaks of file descriptor: " + TEST_FILE); - let openedFile = await OS.File.open(TEST_FILE, { create: true }); - info("At this stage, we leak the file"); - Assert.ok(await testLeaksOf(TEST_FILE, "test.shutdown.file.leak")); - await openedFile.close(); - info("At this stage, we don't leak the file anymore"); - Assert.equal( - false, - await testLeaksOf(TEST_FILE, "test.shutdown.file.leak.2") - ); -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_telemetry.js +++ /dev/null @@ -1,61 +0,0 @@ -"use strict"; - -var { - OS: { File, Path, Constants }, -} = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -// Ensure that we have a profile but that the OS.File worker is not launched -add_task(async function init() { - do_get_profile(); - await File.resetWorker(); -}); - -function getCount(histogram) { - if (histogram == null) { - return 0; - } - - let total = 0; - for (let i of Object.values(histogram.values)) { - total += i; - } - return total; -} - -// Ensure that launching the OS.File worker adds data to the relevant -// histograms -add_task(async function test_startup() { - let LAUNCH = "OSFILE_WORKER_LAUNCH_MS"; - let READY = "OSFILE_WORKER_READY_MS"; - - let before = Services.telemetry.getSnapshotForHistograms("main", false) - .parent; - - // Launch the OS.File worker - await File.getCurrentDirectory(); - - let after = Services.telemetry.getSnapshotForHistograms("main", false).parent; - - info("Ensuring that we have recorded measures for histograms"); - Assert.equal(getCount(after[LAUNCH]), getCount(before[LAUNCH]) + 1); - Assert.equal(getCount(after[READY]), getCount(before[READY]) + 1); - - info("Ensuring that launh <= ready"); - Assert.ok(after[LAUNCH].sum <= after[READY].sum); -}); - -// Ensure that calling writeAtomic adds data to the relevant histograms -add_task(async function test_writeAtomic() { - let LABEL = "OSFILE_WRITEATOMIC_JANK_MS"; - - let before = Services.telemetry.getSnapshotForHistograms("main", false) - .parent; - - // Perform a write. - let path = Path.join(Constants.Path.profileDir, "test_osfile_telemetry.tmp"); - await File.writeAtomic(path, LABEL, { tmpPath: path + ".tmp" }); - - let after = Services.telemetry.getSnapshotForHistograms("main", false).parent; - - Assert.equal(getCount(after[LABEL]), getCount(before[LABEL]) + 1); -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_unique.js +++ /dev/null @@ -1,87 +0,0 @@ -"use strict"; - -const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); - -function run_test() { - do_get_profile(); - run_next_test(); -} - -function testFiles(filename) { - return (async function() { - const MAX_TRIES = 10; - let profileDir = OS.Constants.Path.profileDir; - let path = OS.Path.join(profileDir, filename); - - // Ensure that openUnique() uses the file name if there is no file with that name already. - let openedFile = await OS.File.openUnique(path); - info("\nCreate new file: " + openedFile.path); - await openedFile.file.close(); - let exists = await OS.File.exists(openedFile.path); - Assert.ok(exists); - Assert.equal(path, openedFile.path); - let fileInfo = await OS.File.stat(openedFile.path); - Assert.ok(fileInfo.size == 0); - - // Ensure that openUnique() creates a new file name using a HEX number, as the original name is already taken. - openedFile = await OS.File.openUnique(path); - info("\nCreate unique HEX file: " + openedFile.path); - await openedFile.file.close(); - exists = await OS.File.exists(openedFile.path); - Assert.ok(exists); - fileInfo = await OS.File.stat(openedFile.path); - Assert.ok(fileInfo.size == 0); - - // Ensure that openUnique() generates different file names each time, using the HEX number algorithm - let filenames = new Set(); - for (let i = 0; i < MAX_TRIES; i++) { - openedFile = await OS.File.openUnique(path); - await openedFile.file.close(); - filenames.add(openedFile.path); - } - - Assert.equal(filenames.size, MAX_TRIES); - - // Ensure that openUnique() creates a new human readable file name using, as the original name is already taken. - openedFile = await OS.File.openUnique(path, { humanReadable: true }); - info("\nCreate unique Human Readable file: " + openedFile.path); - await openedFile.file.close(); - exists = await OS.File.exists(openedFile.path); - Assert.ok(exists); - fileInfo = await OS.File.stat(openedFile.path); - Assert.ok(fileInfo.size == 0); - - // Ensure that openUnique() generates different human readable file names each time - filenames = new Set(); - for (let i = 0; i < MAX_TRIES; i++) { - openedFile = await OS.File.openUnique(path, { humanReadable: true }); - await openedFile.file.close(); - filenames.add(openedFile.path); - } - - Assert.equal(filenames.size, MAX_TRIES); - - let exn; - try { - for (let i = 0; i < 100; i++) { - openedFile = await OS.File.openUnique(path, { humanReadable: true }); - await openedFile.file.close(); - } - } catch (ex) { - exn = ex; - } - - info("Ensure that this raises the correct error"); - Assert.ok(!!exn); - Assert.ok(exn instanceof OS.File.Error); - Assert.ok(exn.becauseExists); - })(); -} - -add_task(async function test_unique() { - OS.Shared.DEBUG = true; - // Tests files with extension - await testFiles("dummy_unique_file.txt"); - // Tests files with no extension - await testFiles("dummy_unique_file_no_ext"); -});
deleted file mode 100644 --- a/toolkit/components/osfile/tests/xpcshell/xpcshell.ini +++ /dev/null @@ -1,48 +0,0 @@ -[DEFAULT] -head = head.js - -[test_compression.js] -[test_constants.js] -[test_duration.js] -[test_exception.js] -[test_file_URL_conversion.js] -[test_logging.js] -[test_makeDir.js] -[test_open.js] -[test_osfile_async.js] -[test_osfile_async_append.js] -[test_osfile_async_bytes.js] -[test_osfile_async_copy.js] -[test_osfile_async_flush.js] -[test_osfile_async_largefiles.js] -[test_osfile_async_setDates.js] -# Unimplemented on Windows (bug 1022816). -# Spurious failure on Android test farm due to non-POSIX behavior of -# filesystem backing /mnt/sdcard (not worth trying to fix). -[test_osfile_async_setPermissions.js] -skip-if = os == "win" || os == "android" -[test_osfile_closed.js] -[test_osfile_error.js] -[test_osfile_kill.js] -# Windows test -[test_osfile_win_async_setPermissions.js] -skip-if = os != "win" -[test_osfile_writeAtomic_backupTo_option.js] -[test_osfile_writeAtomic_zerobytes.js] -[test_osfile_writeAtomic_unicode_filename.js] -[test_path.js] -[test_path_constants.js] -[test_queue.js] -[test_read_write.js] -requesttimeoutfactor = 4 -[test_remove.js] -[test_removeDir.js] -requesttimeoutfactor = 4 -[test_removeEmptyDir.js] -[test_reset.js] -[test_shutdown.js] -[test_telemetry.js] -# On Android, we use OS.File during xpcshell initialization, so the expected -# telemetry cannot be observed. -skip-if = toolkit == "android" -[test_unique.js]
--- a/tools/esmify/map.json +++ b/tools/esmify/map.json @@ -852,25 +852,16 @@ "resource://gre/modules/mozIntl.jsm": "toolkit/components/mozintl/mozIntl.jsm", "resource://gre/modules/narrate/NarrateControls.jsm": "toolkit/components/narrate/NarrateControls.jsm", "resource://gre/modules/narrate/Narrator.jsm": "toolkit/components/narrate/Narrator.jsm", "resource://gre/modules/narrate/VoiceSelect.jsm": "toolkit/components/narrate/VoiceSelect.jsm", "resource://gre/modules/netwerk-dns/PublicSuffixList.jsm": "netwerk/dns/PublicSuffixList.jsm", "resource://gre/modules/nsAsyncShutdown.jsm": "toolkit/components/asyncshutdown/nsAsyncShutdown.jsm", "resource://gre/modules/nsCrashMonitor.jsm": "toolkit/components/crashmonitor/nsCrashMonitor.jsm", "resource://gre/modules/nsFormAutoCompleteResult.jsm": "toolkit/components/satchel/nsFormAutoCompleteResult.jsm", - "resource://gre/modules/osfile.jsm": "toolkit/components/osfile/osfile.jsm", - "resource://gre/modules/osfile/osfile_async_front.jsm": "toolkit/components/osfile/modules/osfile_async_front.jsm", - "resource://gre/modules/osfile/osfile_native.jsm": "toolkit/components/osfile/modules/osfile_native.jsm", - "resource://gre/modules/osfile/osfile_shared_allthreads.jsm": "toolkit/components/osfile/modules/osfile_shared_allthreads.jsm", - "resource://gre/modules/osfile/osfile_unix_allthreads.jsm": "toolkit/components/osfile/modules/osfile_unix_allthreads.jsm", - "resource://gre/modules/osfile/osfile_win_allthreads.jsm": "toolkit/components/osfile/modules/osfile_win_allthreads.jsm", - "resource://gre/modules/osfile/ospath.jsm": "toolkit/components/osfile/modules/ospath.jsm", - "resource://gre/modules/osfile/ospath_unix.jsm": "toolkit/components/osfile/modules/ospath_unix.jsm", - "resource://gre/modules/osfile/ospath_win.jsm": "toolkit/components/osfile/modules/ospath_win.jsm", "resource://gre/modules/pdfjs.js": "toolkit/components/pdfjs/pdfjs.js", "resource://gre/modules/policies/WindowsGPOParser.jsm": "toolkit/components/enterprisepolicies/WindowsGPOParser.jsm", "resource://gre/modules/policies/macOSPoliciesParser.jsm": "toolkit/components/enterprisepolicies/macOSPoliciesParser.jsm", "resource://gre/modules/psm/DER.jsm": "security/manager/ssl/DER.jsm", "resource://gre/modules/psm/RemoteSecuritySettings.jsm": "security/manager/ssl/RemoteSecuritySettings.jsm", "resource://gre/modules/psm/X509.jsm": "security/manager/ssl/X509.jsm", "resource://gre/modules/reader/ReaderWorker.jsm": "toolkit/components/reader/ReaderWorker.jsm", "resource://gre/modules/reflect.jsm": "toolkit/components/reflect/reflect.jsm",
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js +++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js @@ -164,17 +164,16 @@ module.exports = { "mozilla/no-useless-parameters": "error", "mozilla/no-useless-removeEventListener": "error", "mozilla/prefer-boolean-length-check": "error", "mozilla/prefer-formatValues": "error", "mozilla/reject-addtask-only": "error", "mozilla/reject-chromeutils-import-params": "error", "mozilla/reject-importGlobalProperties": ["error", "allownonwebidl"], "mozilla/reject-multiple-getters-calls": "error", - "mozilla/reject-osfile": "warn", "mozilla/reject-scriptableunicodeconverter": "warn", "mozilla/rejects-requires-await": "error", "mozilla/use-cc-etc": "error", "mozilla/use-chromeutils-generateqi": "error", "mozilla/use-chromeutils-import": "error", "mozilla/use-default-preference-values": "error", "mozilla/use-includes-instead-of-indexOf": "error", "mozilla/use-isInstance": "error",
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/index.js +++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/index.js @@ -67,17 +67,16 @@ module.exports = { "reject-eager-module-in-lazy-getter": require("../lib/rules/reject-eager-module-in-lazy-getter"), "reject-global-this": require("../lib/rules/reject-global-this"), "reject-globalThis-modification": require("../lib/rules/reject-globalThis-modification"), "reject-import-system-module-from-non-system": require("../lib/rules/reject-import-system-module-from-non-system"), "reject-importGlobalProperties": require("../lib/rules/reject-importGlobalProperties"), "reject-lazy-imports-into-globals": require("../lib/rules/reject-lazy-imports-into-globals"), "reject-mixing-eager-and-lazy": require("../lib/rules/reject-mixing-eager-and-lazy"), "reject-multiple-getters-calls": require("../lib/rules/reject-multiple-getters-calls"), - "reject-osfile": require("../lib/rules/reject-osfile"), "reject-scriptableunicodeconverter": require("../lib/rules/reject-scriptableunicodeconverter"), "reject-relative-requires": require("../lib/rules/reject-relative-requires"), "reject-some-requires": require("../lib/rules/reject-some-requires"), "reject-top-level-await": require("../lib/rules/reject-top-level-await"), "rejects-requires-await": require("../lib/rules/rejects-requires-await"), "use-cc-etc": require("../lib/rules/use-cc-etc"), "use-chromeutils-generateqi": require("../lib/rules/use-chromeutils-generateqi"), "use-chromeutils-import": require("../lib/rules/use-chromeutils-import"),
deleted file mode 100644 --- a/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/reject-osfile.js +++ /dev/null @@ -1,52 +0,0 @@ -/** - * @fileoverview Reject calls into OS.File. We're phasing this out in - * favour of IOUtils. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -"use strict"; - -const { maybeGetMemberPropertyName } = require("../helpers"); - -function isIdentifier(node, id) { - return node && node.type === "Identifier" && node.name === id; -} - -function isOSProp(expr, prop) { - return ( - maybeGetMemberPropertyName(expr.object) === "OS" && - isIdentifier(expr.property, prop) - ); -} - -module.exports = { - meta: { - docs: { - url: - "https://firefox-source-docs.mozilla.org/code-quality/lint/linters/eslint-plugin-mozilla/reject-osfile.html", - }, - schema: [], - type: "problem", - }, - - create(context) { - return { - MemberExpression(node) { - if (isOSProp(node, "File")) { - context.report({ - node, - message: "OS.File is deprecated. You should use IOUtils instead.", - }); - } else if (isOSProp(node, "Path")) { - context.report({ - node, - message: "OS.Path is deprecated. You should use PathUtils instead.", - }); - } - }, - }; - }, -};
deleted file mode 100644 --- a/tools/lint/eslint/eslint-plugin-mozilla/tests/reject-osfile.js +++ /dev/null @@ -1,50 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -// ------------------------------------------------------------------------------ -// Requirements -// ------------------------------------------------------------------------------ - -var rule = require("../lib/rules/reject-osfile"); -var RuleTester = require("eslint").RuleTester; - -const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: "latest" } }); - -// ------------------------------------------------------------------------------ -// Tests -// ------------------------------------------------------------------------------ - -function invalidError(os, util) { - let message = `${os} is deprecated. You should use ${util} instead.`; - return [{ message, type: "MemberExpression" }]; -} - -ruleTester.run("reject-osfile", rule, { - valid: ["IOUtils.write()"], - invalid: [ - { - code: "OS.File.write()", - errors: invalidError("OS.File", "IOUtils"), - }, - { - code: "lazy.OS.File.write()", - errors: invalidError("OS.File", "IOUtils"), - }, - ], -}); - -ruleTester.run("reject-osfile", rule, { - valid: ["PathUtils.join()"], - invalid: [ - { - code: "OS.Path.join()", - errors: invalidError("OS.Path", "PathUtils"), - }, - { - code: "lazy.OS.Path.join()", - errors: invalidError("OS.Path", "PathUtils"), - }, - ], -});