зеркало из https://github.com/mozilla/gecko-dev.git
196 строки
6.0 KiB
JavaScript
196 строки
6.0 KiB
JavaScript
/* 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/. */
|
|
|
|
var EXPORTED_SYMBOLS = [ "ZipUtils" ];
|
|
|
|
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
|
|
ChromeUtils.defineModuleGetter(this, "FileUtils",
|
|
"resource://gre/modules/FileUtils.jsm");
|
|
ChromeUtils.defineModuleGetter(this, "OS",
|
|
"resource://gre/modules/osfile.jsm");
|
|
|
|
|
|
// The maximum amount of file data to buffer at a time during file extraction
|
|
const EXTRACTION_BUFFER = 1024 * 512;
|
|
|
|
|
|
/**
|
|
* Asynchronously writes data from an nsIInputStream to an OS.File instance.
|
|
* The source stream and OS.File are closed regardless of whether the operation
|
|
* succeeds or fails.
|
|
* Returns a promise that will be resolved when complete.
|
|
*
|
|
* @param aPath
|
|
* The name of the file being extracted for logging purposes.
|
|
* @param aStream
|
|
* The source nsIInputStream.
|
|
* @param aFile
|
|
* The open OS.File instance to write to.
|
|
*/
|
|
function saveStreamAsync(aPath, aStream, aFile) {
|
|
return new Promise((resolve, reject) => {
|
|
// Read the input stream on a background thread
|
|
let sts = Cc["@mozilla.org/network/stream-transport-service;1"].
|
|
getService(Ci.nsIStreamTransportService);
|
|
let transport = sts.createInputTransport(aStream, true);
|
|
let input = transport.openInputStream(0, 0, 0)
|
|
.QueryInterface(Ci.nsIAsyncInputStream);
|
|
let source = Cc["@mozilla.org/binaryinputstream;1"].
|
|
createInstance(Ci.nsIBinaryInputStream);
|
|
source.setInputStream(input);
|
|
|
|
|
|
function readFailed(error) {
|
|
try {
|
|
aStream.close();
|
|
} catch (e) {
|
|
Cu.reportError("Failed to close JAR stream for " + aPath);
|
|
}
|
|
|
|
aFile.close().then(function() {
|
|
reject(error);
|
|
}, function(e) {
|
|
Cu.reportError("Failed to close file for " + aPath);
|
|
reject(error);
|
|
});
|
|
}
|
|
|
|
function readData() {
|
|
try {
|
|
let count = Math.min(source.available(), EXTRACTION_BUFFER);
|
|
let data = new Uint8Array(count);
|
|
source.readArrayBuffer(count, data.buffer);
|
|
|
|
aFile.write(data, { bytes: count }).then(function() {
|
|
input.asyncWait(readData, 0, 0, Services.tm.currentThread);
|
|
}, readFailed);
|
|
} catch (e) {
|
|
if (e.result == Cr.NS_BASE_STREAM_CLOSED)
|
|
resolve(aFile.close());
|
|
else
|
|
readFailed(e);
|
|
}
|
|
}
|
|
|
|
input.asyncWait(readData, 0, 0, Services.tm.currentThread);
|
|
});
|
|
}
|
|
|
|
|
|
var ZipUtils = {
|
|
|
|
/**
|
|
* Asynchronously extracts files from a ZIP file into a directory.
|
|
* Returns a promise that will be resolved when the extraction is complete.
|
|
*
|
|
* @param aZipFile
|
|
* The source ZIP file that contains the add-on.
|
|
* @param aDir
|
|
* The nsIFile to extract to.
|
|
*/
|
|
extractFilesAsync: function ZipUtils_extractFilesAsync(aZipFile, aDir) {
|
|
let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].
|
|
createInstance(Ci.nsIZipReader);
|
|
|
|
try {
|
|
zipReader.open(aZipFile);
|
|
} catch (e) {
|
|
return Promise.reject(e);
|
|
}
|
|
|
|
return (async function() {
|
|
// Get all of the entries in the zip and sort them so we create directories
|
|
// before files
|
|
let names = Array.from(zipReader.findEntries(null));
|
|
names.sort();
|
|
|
|
for (let name of names) {
|
|
let entryName = name;
|
|
let zipentry = zipReader.getEntry(name);
|
|
let path = OS.Path.join(aDir.path, ...name.split("/"));
|
|
|
|
if (zipentry.isDirectory) {
|
|
try {
|
|
await OS.File.makeDir(path);
|
|
} catch (e) {
|
|
dump("extractFilesAsync: failed to create directory " + path + "\n");
|
|
throw e;
|
|
}
|
|
} else {
|
|
let options = { unixMode: zipentry.permissions | FileUtils.PERMS_FILE };
|
|
try {
|
|
let file = await OS.File.open(path, { truncate: true }, options);
|
|
if (zipentry.realSize == 0)
|
|
await file.close();
|
|
else
|
|
await saveStreamAsync(path, zipReader.getInputStream(entryName), file);
|
|
} catch (e) {
|
|
dump("extractFilesAsync: failed to extract file " + path + "\n");
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
|
|
zipReader.close();
|
|
})().catch((e) => {
|
|
zipReader.close();
|
|
throw e;
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Extracts files from a ZIP file into a directory.
|
|
*
|
|
* @param aZipFile
|
|
* The source ZIP file that contains the add-on.
|
|
* @param aDir
|
|
* The nsIFile to extract to.
|
|
*/
|
|
extractFiles: function ZipUtils_extractFiles(aZipFile, aDir) {
|
|
function getTargetFile(aDir, entry) {
|
|
let target = aDir.clone();
|
|
entry.split("/").forEach(function(aPart) {
|
|
target.append(aPart);
|
|
});
|
|
return target;
|
|
}
|
|
|
|
let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].
|
|
createInstance(Ci.nsIZipReader);
|
|
zipReader.open(aZipFile);
|
|
|
|
try {
|
|
// create directories first
|
|
for (let entryName of zipReader.findEntries("*/")) {
|
|
let target = getTargetFile(aDir, entryName);
|
|
if (!target.exists()) {
|
|
try {
|
|
target.create(Ci.nsIFile.DIRECTORY_TYPE,
|
|
FileUtils.PERMS_DIRECTORY);
|
|
} catch (e) {
|
|
dump("extractFiles: failed to create target directory for extraction file = " + target.path + "\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
for (let entryName of zipReader.findEntries(null)) {
|
|
let target = getTargetFile(aDir, entryName);
|
|
if (target.exists())
|
|
continue;
|
|
|
|
zipReader.extract(entryName, target);
|
|
try {
|
|
target.permissions |= FileUtils.PERMS_FILE;
|
|
} catch (e) {
|
|
dump("Failed to set permissions " + FileUtils.PERMS_FILE.toString(8) + " on " + target.path + " " + e + "\n");
|
|
}
|
|
}
|
|
} finally {
|
|
zipReader.close();
|
|
}
|
|
},
|
|
|
|
};
|