back out f24c22852083 for xpcshell failures b=829832

--HG--
extra : rebase_source : 151e69e6a280c9c67e9b49d55c11aad1fc0def94
This commit is contained in:
Karl Tomlinson 2013-02-20 11:15:51 +13:00
parent 7bf4102824
commit 6cf98de946
5 changed files with 14 additions and 314 deletions

View File

@ -34,9 +34,7 @@ interface nsIFile;
*
* @remarks Implementations may require the consumer to always call Finish. If
* the object reference is released without calling Finish, a memory
* leak may occur, and the target file might be kept locked. All
* public methods of the interface may only be called from the main
* thread.
* leak may occur, and the target file might be kept locked.
*/
[scriptable, uuid(f2fb4daf-0aba-4b30-9ec0-b7a83ce3b7ab)]
interface nsIBackgroundFileSaver : nsISupports
@ -51,22 +49,6 @@ interface nsIBackgroundFileSaver : nsISupports
*/
attribute nsIBackgroundFileSaverObserver observer;
/**
* The SHA256 hash in raw bytes associated with the file that was downloaded.
*
* @remarks Reading this will throw NS_ERROR_NOT_AVAILABLE unless
* sha256enabled is true and onSaveComplete has been called.
*/
readonly attribute ACString sha256Hash;
/**
* Compute SHA256.
*
* @remarks This must be set on the main thread before the first call to
* setTarget.
*/
void enableSha256();
/**
* Sets the name of the output file to be written. The output file may
* already exist, in which case it will be overwritten. The target can be

View File

@ -4,18 +4,11 @@
* 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/RefPtr.h"
#include "pk11pub.h"
#include "ScopedNSSTypes.h"
#include "secoidt.h"
#include "BackgroundFileSaver.h"
#include "nsIFile.h"
#include "nsIPipe.h"
#include "nsNetUtil.h"
#include "nsThreadUtils.h"
#include "nsXPCOMStrings.h"
#include "BackgroundFileSaver.h"
namespace mozilla {
namespace net {
@ -81,36 +74,13 @@ BackgroundFileSaver::BackgroundFileSaver()
, mAssignedTarget(nullptr)
, mAssignedTargetKeepPartial(false)
, mAsyncCopyContext(nullptr)
, mSha256Enabled(false)
, mActualTarget(nullptr)
, mActualTargetKeepPartial(false)
, mDigestContext(nullptr)
{
}
BackgroundFileSaver::~BackgroundFileSaver()
{
destructorSafeDestroyNSSReference();
shutdown(calledFromObject);
}
void
BackgroundFileSaver::destructorSafeDestroyNSSReference()
{
nsNSSShutDownPreventionLock lock;
if (isAlreadyShutDown()) {
return;
}
if (mDigestContext) {
mozilla::psm::PK11_DestroyContext_true(mDigestContext.forget());
mDigestContext = nullptr;
}
}
void
BackgroundFileSaver::virtualDestroyNSSReference()
{
destructorSafeDestroyNSSReference();
}
// Called on the control thread.
@ -156,6 +126,7 @@ NS_IMETHODIMP
BackgroundFileSaver::SetTarget(nsIFile *aTarget, bool aKeepPartial)
{
NS_ENSURE_ARG(aTarget);
{
MutexAutoLock lock(mLock);
aTarget->Clone(getter_AddRefs(mAssignedTarget));
@ -196,33 +167,6 @@ BackgroundFileSaver::Finish(nsresult aStatus)
return GetWorkerThreadAttention(NS_FAILED(aStatus));
}
NS_IMETHODIMP
BackgroundFileSaver::EnableSha256()
{
MOZ_ASSERT(NS_IsMainThread(),
"Can't enable sha256 or initialize NSS off the main thread");
mSha256Enabled = true;
// Ensure Personal Security Manager is initialized. This is required for
// PK11_* operations to work.
nsresult rv;
nsCOMPtr<nsISupports> nssDummy = do_GetService("@mozilla.org/psm;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHODIMP
BackgroundFileSaver::GetSha256Hash(nsACString& aHash)
{
MOZ_ASSERT(NS_IsMainThread(), "Can't inspect sha256 off the main thread");
// We acquire a lock because mSha256 is written on the worker thread.
MutexAutoLock lock(mLock);
if (mSha256.IsEmpty()) {
return NS_ERROR_NOT_AVAILABLE;
}
aHash = mSha256;
return NS_OK;
}
// Called on the control thread.
nsresult
BackgroundFileSaver::GetWorkerThreadAttention(bool aShouldInterruptCopy)
@ -271,6 +215,7 @@ void
BackgroundFileSaver::AsyncCopyCallback(void *aClosure, nsresult aStatus)
{
BackgroundFileSaver *self = (BackgroundFileSaver *)aClosure;
{
MutexAutoLock lock(self->mLock);
@ -320,6 +265,7 @@ BackgroundFileSaver::ProcessAttention()
NS_CancelAsyncCopy(mAsyncCopyContext, NS_ERROR_ABORT);
return NS_OK;
}
// Use the current shared state to determine the next operation to execute.
rv = ProcessStateChange();
if (NS_FAILED(rv)) {
@ -331,6 +277,7 @@ BackgroundFileSaver::ProcessAttention()
mStatus = rv;
}
}
// Ensure we notify completion now that the operation failed.
CheckCompletion();
}
@ -352,7 +299,6 @@ BackgroundFileSaver::ProcessStateChange()
// Get a copy of the current shared state for the worker thread.
nsCOMPtr<nsIFile> target;
bool targetKeepPartial;
bool sha256Enabled = false;
{
MutexAutoLock lock(mLock);
@ -361,8 +307,6 @@ BackgroundFileSaver::ProcessStateChange()
// From now on, another attention event needs to be posted if state changes.
mWorkerThreadAttentionRequested = false;
sha256Enabled = mSha256Enabled;
}
// The target can only be null if it has never been assigned. In this case,
@ -460,30 +404,6 @@ BackgroundFileSaver::ProcessStateChange()
return NS_ERROR_FAILURE;
}
// Wrap the output stream in a hashing stream if hashing is enabled and NSS
// hasn't been shut down.
bool isShutDown = false;
if (sha256Enabled && !mDigestContext) {
nsNSSShutDownPreventionLock lock;
if (!(isShutDown = isAlreadyShutDown())) {
mDigestContext =
PK11_CreateDigestContext(static_cast<SECOidTag>(SEC_OID_SHA256));
NS_ENSURE_TRUE(mDigestContext, NS_ERROR_OUT_OF_MEMORY);
}
}
MOZ_ASSERT(!sha256Enabled || mDigestContext || isShutDown,
"Hashing enabled but creating digest context didn't work");
if (mDigestContext) {
// No need to acquire the NSS lock here, DigestOutputStream must acquire it
// in any case before each asynchronous write. Constructing the
// DigestOutputStream cannot fail. Passing mDigestContext to
// DigestOutputStream is safe, because BackgroundFileSaver always outlives
// the outputStream. BackgroundFileSaver is reference-counted before the
// call to AsyncCopy, and mDigestContext is never destroyed before
// AsyncCopyCallback.
outputStream = new DigestOutputStream(outputStream, mDigestContext);
}
// Start copying our input to the target file. No errors can be raised past
// this point if the copy starts, since they should be handled by the thread.
{
@ -554,26 +474,11 @@ BackgroundFileSaver::CheckCompletion()
mComplete = true;
}
// Ensure we notify completion now that the operation finished.
// Do a best-effort attempt to remove the file if required.
if (failed && mActualTarget && !mActualTargetKeepPartial) {
(void)mActualTarget->Remove(false);
}
// Finish computing the hash
if (!failed && mDigestContext) {
nsNSSShutDownPreventionLock lock;
if (!isAlreadyShutDown()) {
Digest d;
rv = d.End(SEC_OID_SHA256, mDigestContext);
if (NS_SUCCEEDED(rv)) {
MutexAutoLock lock(mLock);
mSha256 = nsDependentCSubstring(char_ptr_cast(d.get().data),
d.get().len);
}
}
}
// Post an event to notify that the operation completed.
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(this, &BackgroundFileSaver::NotifySaveComplete);
@ -890,76 +795,5 @@ BackgroundFileSaverStreamListener::NotifySuspendOrResume()
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
//// DigestOutputStream
NS_IMPL_THREADSAFE_ISUPPORTS1(DigestOutputStream,
nsIOutputStream)
DigestOutputStream::DigestOutputStream(nsIOutputStream* aStream,
PK11Context* aContext) :
mOutputStream(aStream)
, mDigestContext(aContext)
{
MOZ_ASSERT(mDigestContext, "Can't have null digest context");
MOZ_ASSERT(mOutputStream, "Can't have null output stream");
}
DigestOutputStream::~DigestOutputStream()
{
shutdown(calledFromObject);
}
NS_IMETHODIMP
DigestOutputStream::Close()
{
return mOutputStream->Close();
}
NS_IMETHODIMP
DigestOutputStream::Flush()
{
return mOutputStream->Flush();
}
NS_IMETHODIMP
DigestOutputStream::Write(const char* aBuf, uint32_t aCount, uint32_t* retval)
{
nsNSSShutDownPreventionLock lock;
if (isAlreadyShutDown()) {
return NS_ERROR_NOT_AVAILABLE;
}
nsresult rv = MapSECStatus(PK11_DigestOp(mDigestContext,
uint8_t_ptr_cast(aBuf), aCount));
NS_ENSURE_SUCCESS(rv, rv);
return mOutputStream->Write(aBuf, aCount, retval);
}
NS_IMETHODIMP
DigestOutputStream::WriteFrom(nsIInputStream* aFromStream,
uint32_t aCount, uint32_t* retval)
{
// Not supported. We could read the stream to a buf, call DigestOp on the
// result, seek back and pass the stream on, but it's not worth it since our
// application (NS_AsyncCopy) doesn't invoke this on the sink.
MOZ_NOT_REACHED("DigestOutputStream::WriteFrom not implemented");
}
NS_IMETHODIMP
DigestOutputStream::WriteSegments(nsReadSegmentFun aReader,
void *aClosure, uint32_t aCount,
uint32_t *retval)
{
MOZ_NOT_REACHED("DigestOutputStream::WriteSegments not implemented");
}
NS_IMETHODIMP
DigestOutputStream::IsNonBlocking(bool *retval)
{
return mOutputStream->IsNonBlocking(retval);
}
} // namespace net
} // namespace mozilla

View File

@ -14,12 +14,10 @@
#include "mozilla/Mutex.h"
#include "nsCOMPtr.h"
#include "nsNSSShutDown.h"
#include "nsIAsyncOutputStream.h"
#include "nsIBackgroundFileSaver.h"
#include "nsIStreamListener.h"
#include "nsStreamUtils.h"
#include "ScopedNSSTypes.h"
class nsIAsyncInputStream;
class nsIThread;
@ -27,13 +25,10 @@ class nsIThread;
namespace mozilla {
namespace net {
class DigestOutputStream;
////////////////////////////////////////////////////////////////////////////////
//// BackgroundFileSaver
class BackgroundFileSaver : public nsIBackgroundFileSaver,
public nsNSSShutDownObject
class BackgroundFileSaver : public nsIBackgroundFileSaver
{
public:
NS_DECL_NSIBACKGROUNDFILESAVER
@ -48,19 +43,9 @@ public:
*/
nsresult Init();
/**
* Used by nsNSSShutDownList to manage nsNSSShutDownObjects.
*/
void virtualDestroyNSSReference();
protected:
virtual ~BackgroundFileSaver();
/**
* Helper function for managing NSS objects (mDigestContext).
*/
void destructorSafeDestroyNSSReference();
/**
* Thread that constructed this object.
*/
@ -73,8 +58,7 @@ protected:
/**
* Stream that receives data from derived classes. The received data will be
* available to the worker thread through mPipeInputStream. This is an
* instance of nsPipeOutputStream, not BackgroundFileSaverOutputStream.
* available to the worker thread through mPipeInputStream.
*/
nsCOMPtr<nsIAsyncOutputStream> mPipeOutputStream;
@ -158,18 +142,6 @@ private:
*/
nsCOMPtr<nsISupports> mAsyncCopyContext;
/**
* The SHA 256 hash in raw bytes of the downloaded file. This is written
* by the worker thread but can be read on the main thread.
*/
nsAutoCString mSha256;
/**
* Whether or not to compute the hash. Must be set on the main thread before
* setTarget is called.
*/
bool mSha256Enabled;
//////////////////////////////////////////////////////////////////////////////
//// State handled exclusively by the worker thread
@ -184,12 +156,6 @@ private:
*/
bool mActualTargetKeepPartial;
/**
* Used to calculate the file hash. This keeps state across file renames and
* is lazily initialized in ProcessStateChange.
*/
ScopedPK11Context mDigestContext;
//////////////////////////////////////////////////////////////////////////////
//// Private methods
@ -271,8 +237,7 @@ private:
};
////////////////////////////////////////////////////////////////////////////////
//// BackgroundFileSaverStreamListener. This class is instantiated by
// nsExternalHelperAppService, DownloadCore.jsm, and possibly others.
//// BackgroundFileSaverStreamListener
class BackgroundFileSaverStreamListener : public BackgroundFileSaver
, public nsIStreamListener
@ -323,31 +288,6 @@ private:
nsresult NotifySuspendOrResume();
};
// A wrapper around nsIOutputStream, so that we can compute hashes on the
// stream without copying and without polluting pristine NSS code with XPCOM
// interfaces.
class DigestOutputStream : public nsNSSShutDownObject,
public nsIOutputStream
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOUTPUTSTREAM
// Constructor. Neither parameter may be null. The caller owns both.
DigestOutputStream(nsIOutputStream* outputStream, PK11Context* aContext);
~DigestOutputStream();
// We don't own any NSS objects here, so no need to clean up
void virtualDestroyNSSReference() { }
private:
// Calls to write are passed to this stream.
nsCOMPtr<nsIOutputStream> mOutputStream;
// Digest context used to compute the hash, owned by the caller.
PK11Context* mDigestContext;
// Don't accidentally copy construct.
DigestOutputStream(const DigestOutputStream& d);
};
} // namespace net
} // namespace mozilla

View File

@ -45,16 +45,6 @@ const TEST_FILE_NAME_1 = "test-backgroundfilesaver-1.txt";
const TEST_FILE_NAME_2 = "test-backgroundfilesaver-2.txt";
const TEST_FILE_NAME_3 = "test-backgroundfilesaver-3.txt";
// A map of test data length to the expected hash
const EXPECTED_HASHES = {
// SHA-256 hash of TEST_DATA_SHORT
40 : "f37176b690e8744ee990a206c086cba54d1502aa2456c3b0c84ef6345d72a192",
// SHA-256 hash of TEST_DATA_SHORT + TEST_DATA_SHORT
80 : "780c0e91f50bb7ec922cc11e16859e6d5df283c0d9470f61772e3d79f41eeb58",
// SHA-256 hash of a bunch of dashes
16777216 : "03a0db69a30140f307587ee746a539247c181bafd85b85c8516a3533c7d9ea1d"
};
const gTextDecoder = new TextDecoder();
// Generate a long string of data in a moderately fast way.
@ -77,21 +67,6 @@ function getTempFile(aLeafName) {
return file;
}
/**
* Helper function for converting a binary blob to its hex equivalent.
*
* @param str
* String possibly containing non-printable chars.
* @return A hex-encoded string.
*/
function toHex(str) {
var hex = '';
for (var i = 0; i < str.length; i++) {
hex += ('0' + str.charCodeAt(i).toString(16)).slice(-2);
}
return hex;
}
/**
* Ensures that the given file contents are equal to the given string.
*
@ -323,7 +298,6 @@ add_task(function test_combinations()
let saver = useStreamListener
? new BackgroundFileSaverStreamListener()
: new BackgroundFileSaverOutputStream();
saver.enableSha256();
let completionPromise = promiseSaverComplete(saver, onTargetChange);
// Start feeding the first chunk of data to the saver. In case we are using
@ -366,10 +340,9 @@ add_task(function test_combinations()
if (!cancelAtSomePoint) {
// In this case, the file must exist.
do_check_true(currentFile.exists());
expectedContents = testData + testData;
yield promiseVerifyContents(currentFile, expectedContents);
do_check_eq(EXPECTED_HASHES[expectedContents.length],
toHex(saver.sha256Hash));
if (!cancelAtSomePoint) {
yield promiseVerifyContents(currentFile, testData + testData);
}
currentFile.remove(false);
// If the target was really renamed, the old file should not exist.
@ -406,7 +379,6 @@ add_task(function test_setTarget_after_close_stream()
// where the file already exists.
for (let i = 0; i < 2; i++) {
let saver = new BackgroundFileSaverOutputStream();
saver.enableSha256();
let completionPromise = promiseSaverComplete(saver);
// Copy some data to the output stream of the file saver. This data must
@ -422,8 +394,6 @@ add_task(function test_setTarget_after_close_stream()
// Verify results.
yield promiseVerifyContents(destFile, TEST_DATA_SHORT);
do_check_eq(EXPECTED_HASHES[TEST_DATA_SHORT.length],
toHex(saver.sha256Hash));
}
// Clean up.
@ -468,29 +438,6 @@ add_task(function test_finish_only()
yield completionPromise;
});
add_task(function test_invalid_hash()
{
let saver = new BackgroundFileSaverStreamListener();
// We shouldn't be able to get the hash if hashing hasn't been enabled
try {
let hash = saver.sha256Hash;
throw "Shouldn't be able to get hash if hashing not enabled";
} catch (ex if ex.result == Cr.NS_ERROR_NOT_AVAILABLE) { }
// Enable hashing, but don't feed any data to saver
saver.enableSha256();
let destFile = getTempFile(TEST_FILE_NAME_1);
saver.setTarget(destFile, false);
// We don't wait on promiseSaverComplete, so the hash getter can run before
// or after onSaveComplete is called. However, the expected behavior is the
// same in both cases since the hash is only valid when the save completes
// successfully.
saver.finish(Cr.NS_ERROR_FAILURE);
try {
let hash = saver.sha256Hash;
throw "Shouldn't be able to get hash if save did not succeed";
} catch (ex if ex.result == Cr.NS_ERROR_NOT_AVAILABLE) { }
});
add_task(function test_teardown()
{
gStillRunning = false;

View File

@ -124,13 +124,10 @@ MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPK11Context,
mozilla::psm::PK11_DestroyContext_true)
/** A more convenient way of dealing with digests calculated into
* stack-allocated buffers. NSS must be initialized on the main thread before
* use, and the caller must ensure NSS isn't shut down, typically by
* subclassing nsNSSShutDownObject, while Digest is in use.
* stack-allocated buffers.
*
* Typical usage, for digesting a buffer in memory:
*
* nsCOMPtr<nsISupports> nssDummy = do_GetService("@mozilla.org/psm;1", &rv);
* Digest digest;
* nsresult rv = digest.DigestBuf(SEC_OID_SHA256, mybuffer, myBufferLen);
* NS_ENSURE_SUCCESS(rv, rv);
@ -147,7 +144,7 @@ MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPK11Context,
* rv = MapSECStatus(PK11_DigestOp(digestContext, ...));
* NS_ENSURE_SUCCESS(rv, rv);
* }
* rv = digest.End(SEC_OID_SHA1, digestContext);
* rv = digestContext.End(SEC_OID_SHA1, digestContext);
* NS_ENSURE_SUCCESS(rv, rv)
*/
class Digest