зеркало из https://github.com/mozilla/gecko-dev.git
Long lost patch for bugs 27609,35161, et al; r=mcafee, sr=mscott
This commit is contained in:
Родитель
6568dc534e
Коммит
5a7112f3dd
|
@ -350,6 +350,10 @@ nsBufferedOutputStream::Flush(void)
|
|||
{
|
||||
nsresult rv;
|
||||
PRUint32 amt;
|
||||
if (!mStream) {
|
||||
// Stream already cancelled/flushed; probably because of error.
|
||||
return NS_OK;
|
||||
}
|
||||
rv = Sink()->Write(mBuffer, mCursor, &amt);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
mBufferStartOffset += amt;
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIChannel.idl"
|
||||
#include "nsIFileSpec.idl"
|
||||
|
||||
interface nsIObserver;
|
||||
interface nsILocalFile;
|
||||
|
||||
/*------------------------- nsIStreamTransferOperation -------------------------
|
||||
| This interface is provided to enable the JavaScript portion of the standard |
|
||||
|
@ -37,7 +37,7 @@ interface nsIObserver;
|
|||
[scriptable, uuid(E2200F90-3E23-11d3-806A-00600811A9C3)]
|
||||
interface nsIStreamTransferOperation : nsISupports {
|
||||
readonly attribute nsIChannel source;
|
||||
readonly attribute nsIFileSpec target;
|
||||
readonly attribute nsILocalFile target;
|
||||
|
||||
attribute nsIObserver observer;
|
||||
|
||||
|
@ -56,4 +56,8 @@ interface nsIStreamTransferOperation : nsISupports {
|
|||
const unsigned long kOpRead = 10;
|
||||
const unsigned long kOpCreateFile = 11;
|
||||
const unsigned long kOpAsyncRead = 12;
|
||||
|
||||
// Reason codes (for write errors):
|
||||
const unsigned long kReasonAccessError = 1;
|
||||
const unsigned long kReasonDiskFull = 2;
|
||||
};
|
||||
|
|
|
@ -26,7 +26,8 @@ var dialog;
|
|||
|
||||
function loadDialog() {
|
||||
dialog.location.setAttribute( "value", data.source.URI.spec );
|
||||
dialog.fileName.setAttribute( "value", data.target.nativePath );
|
||||
dialog.fileName.setAttribute( "value", data.target.unicodePath );
|
||||
dialog.error = false;
|
||||
}
|
||||
|
||||
var contractId = "@mozilla.org/appshell/component/xfer;1";
|
||||
|
@ -59,24 +60,22 @@ var observer = {
|
|||
}
|
||||
|
||||
function getString( stringId ) {
|
||||
// Check if we've fetched this string already.
|
||||
if ( !dialog.strings[ stringId ] ) {
|
||||
// Try to get it.
|
||||
var elem = document.getElementById( "dialog.strings."+stringId );
|
||||
if ( elem
|
||||
// Check if we've fetched this string already.
|
||||
if ( !( stringId in dialog.strings ) ) {
|
||||
// Try to get it.
|
||||
var elem = document.getElementById( "dialog.strings."+stringId );
|
||||
if ( elem
|
||||
&&
|
||||
elem.childNodes
|
||||
elem.firstChild
|
||||
&&
|
||||
elem.childNodes[0]
|
||||
&&
|
||||
elem.childNodes[0].nodeValue ) {
|
||||
dialog.strings[ stringId ] = elem.childNodes[0].nodeValue;
|
||||
} else {
|
||||
// If unable to fetch string, use an empty string.
|
||||
dialog.strings[ stringId ] = "";
|
||||
}
|
||||
}
|
||||
return dialog.strings[ stringId ];
|
||||
elem.firstChild.nodeValue ) {
|
||||
dialog.strings[ stringId ] = elem.firstChild.nodeValue;
|
||||
} else {
|
||||
// If unable to fetch string, use an empty string.
|
||||
dialog.strings[ stringId ] = "";
|
||||
}
|
||||
}
|
||||
return dialog.strings[ stringId ];
|
||||
}
|
||||
|
||||
function replaceInsert( text, index, value ) {
|
||||
|
@ -89,8 +88,8 @@ function replaceInsert( text, index, value ) {
|
|||
function onLoad() {
|
||||
// Set global variables.
|
||||
data = window.arguments[0];
|
||||
|
||||
if ( !data ) {
|
||||
dump( "Invalid argument to downloadProgress.xul\n" );
|
||||
window.close()
|
||||
return;
|
||||
}
|
||||
|
@ -111,17 +110,36 @@ function onLoad() {
|
|||
|
||||
// Commence transfer.
|
||||
data.observer = observer;
|
||||
data.Start();
|
||||
try {
|
||||
data.Start();
|
||||
} catch( exception ) {
|
||||
onError( exception );
|
||||
}
|
||||
}
|
||||
|
||||
function onUnload() {
|
||||
// Unhook observer.
|
||||
data.observer = null;
|
||||
// Only do this one time...
|
||||
if ( data ) {
|
||||
// Unhook underlying xfer operation.
|
||||
var op = data;
|
||||
data = null;
|
||||
|
||||
// See if we completed normally (i.e., are closing ourself).
|
||||
if ( !completed ) {
|
||||
// Terminate transfer.
|
||||
data.Stop();
|
||||
// Unhook observer.
|
||||
op.observer = null;
|
||||
|
||||
// See if we completed normally (i.e., are closing ourself).
|
||||
if ( started && !completed ) {
|
||||
// Terminate transfer.
|
||||
try {
|
||||
op.Stop();
|
||||
} catch ( exception ) {
|
||||
if ( !dialog.error ) {
|
||||
// Show error now.
|
||||
onError( exception );
|
||||
}
|
||||
}
|
||||
op = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,7 +159,7 @@ var warningLimit = 30000; // Warn on Cancel after 30 sec (30000 milleseconds).
|
|||
|
||||
function stop() {
|
||||
// If too much time has elapsed, make sure it's OK to quit.
|
||||
if ( started ) {
|
||||
if ( started && !completed ) {
|
||||
// Get current time.
|
||||
var now = ( new Date() ).getTime();
|
||||
|
||||
|
@ -155,7 +173,7 @@ function stop() {
|
|||
}
|
||||
|
||||
// Stop the transfer.
|
||||
data.Stop();
|
||||
onUnload();
|
||||
|
||||
// Close the window.
|
||||
window.close();
|
||||
|
@ -321,11 +339,6 @@ function onCompletion( status ) {
|
|||
// Close the window in 2 seconds (to ensure user sees we're done).
|
||||
window.setTimeout( "window.close();", 2000 );
|
||||
} catch ( exception ) {
|
||||
dump( "Error setting close timeout\n" );
|
||||
// OK, try to just close the window immediately.
|
||||
window.close();
|
||||
// If that's not working either, change button text to give user a clue.
|
||||
dialog.cancel.childNodes[0].nodeValue = "Close";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -336,16 +349,56 @@ function onStatus( status ) {
|
|||
if ( status.length > 9 ) {
|
||||
dialog.status.setAttribute("value", status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onError( errorCode ) {
|
||||
// XXX - l10n
|
||||
var msg = "Unknown error";
|
||||
switch ( errorCode ) {
|
||||
default:
|
||||
msg += " [" + errorCode + "]\n";
|
||||
// Record fact we had an error.
|
||||
dialog.error = true;
|
||||
|
||||
// Errorcode has format: "operation rv <string>" where
|
||||
// operation - operation that failed; values are as defined
|
||||
// in nsIStreamTransferOperation.idl
|
||||
// rv - actual nsresult (in "%X" format)
|
||||
// reason - reason for failure; values are as defined
|
||||
// in nsIStreamTransferOperation.idl
|
||||
var msg;
|
||||
switch ( parseInt( errorCode.split(" ")[0] ) ) {
|
||||
// Write errors...
|
||||
case Components.interfaces.nsIStreamTransferOperation.kOpWrite :
|
||||
case Components.interfaces.nsIStreamTransferOperation.kOpAsyncWrite :
|
||||
case Components.interfaces.nsIStreamTransferOperation.kOpOpenOutputStream :
|
||||
case Components.interfaces.nsIStreamTransferOperation.kOpCreateTransport :
|
||||
case Components.interfaces.nsIStreamTransferOperation.kOpOutputClose :
|
||||
case Components.interfaces.nsIStreamTransferOperation.kOpOutputCancel :
|
||||
// Look for some specific reasons.
|
||||
switch ( parseInt( errorCode.split(" ")[2] ) ) {
|
||||
case Components.interfaces.nsIStreamTransferOperation.kReasonAccessError :
|
||||
// Access/permission error.
|
||||
msg = getString( "accessErrorMsg" );
|
||||
break;
|
||||
|
||||
case Components.interfaces.nsIStreamTransferOperation.kReasonDiskFull :
|
||||
// Out of space error.
|
||||
msg = getString( "diskFullMsg" );
|
||||
break;
|
||||
|
||||
default :
|
||||
// Generic write error.
|
||||
msg = getString( "writeErrorMsg" );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
// Read errors...
|
||||
default :
|
||||
// Presume generic read error.
|
||||
msg = getString( "readErrorMsg" );
|
||||
break;
|
||||
}
|
||||
// Tell user.
|
||||
alert( msg );
|
||||
window.close();
|
||||
// Dump error code to console.
|
||||
dump( "downloadProgress.js onError: " + errorCode + "\n" );
|
||||
// Terminate transfer and clean up.
|
||||
stop();
|
||||
}
|
||||
|
|
|
@ -45,10 +45,7 @@ Contributor(s):
|
|||
<script src="chrome://global/content/downloadProgress.js"></script>
|
||||
|
||||
<!-- This is non-visible content that simply adds translatable string
|
||||
into the document so that it is accessible to JS code.
|
||||
|
||||
XXX-TODO:
|
||||
convert to use string bundles.
|
||||
into the document so that it is accessible to JS code.
|
||||
-->
|
||||
|
||||
<data id="dialog.strings.confirmCancel">&confirmCancel;</data>
|
||||
|
@ -58,6 +55,10 @@ Contributor(s):
|
|||
<data id="dialog.strings.shortTimeFormat">&shortTimeFormat;</data>
|
||||
<data id="dialog.strings.longTimeFormat">&longTimeFormat;</data>
|
||||
<data id="dialog.strings.unknownTime">&unknownTime;</data>
|
||||
<data id="dialog.strings.diskFullMsg">&diskFullMsg;</data>
|
||||
<data id="dialog.strings.accessErrorMsg">&accessErrorMsg;</data>
|
||||
<data id="dialog.strings.writeErrorMsg">&writeErrorMsg;</data>
|
||||
<data id="dialog.strings.readErrorMsg">&readErrorMsg;</data>
|
||||
|
||||
<grid flex="1">
|
||||
<columns>
|
||||
|
@ -116,4 +117,4 @@ Contributor(s):
|
|||
<spring flex="1"/>
|
||||
</box>
|
||||
|
||||
</window>
|
||||
</window>
|
||||
|
|
|
@ -67,6 +67,12 @@
|
|||
#1 will be replaced by the percentage of the file that has been saved -->
|
||||
<!ENTITY percentMsg "#1%">
|
||||
|
||||
<!-- Error messages -->
|
||||
<!ENTITY diskFullMsg "There is not enough free space to store the file. Free up space and try again, or choose a different target location.">
|
||||
<!ENTITY accessErrorMsg "You do not have permission to write to the target location, or, the disk is write-protected. Choose a different target location.">
|
||||
<!ENTITY writeErrorMsg "There was an error writing to the target location.">
|
||||
<!ENTITY readErrorMsg "There was an error reading from the source location.">
|
||||
|
||||
<!ENTITY keepProgressDialogUpMsg.label "Keep this window open after the download is complete.">
|
||||
<!ENTITY openFolder.label "Reveal Location">
|
||||
<!ENTITY open.label "Launch File">
|
||||
|
|
|
@ -70,7 +70,7 @@ protected:
|
|||
|
||||
private:
|
||||
// Put up file picker dialog.
|
||||
NS_IMETHOD SelectFile( nsIDOMWindowInternal *parent, nsIFileSpec **result, const nsCString &suggested );
|
||||
NS_IMETHOD SelectFile( nsIDOMWindowInternal *parent, nsILocalFile **result, const nsCString &suggested );
|
||||
nsCString SuggestNameFor( nsIChannel *aChannel, char const *suggestedName );
|
||||
|
||||
// Objects of this class are counted to manage library unloading...
|
||||
|
@ -119,7 +119,7 @@ nsStreamTransfer::SelectFileAndTransferLocation( nsIChannel *aChannel,
|
|||
char const *contentType,
|
||||
char const *suggestedName ) {
|
||||
// Prompt the user for the destination file.
|
||||
nsCOMPtr<nsIFileSpec> outputFile;
|
||||
nsCOMPtr<nsILocalFile> outputFile;
|
||||
PRBool isValid = PR_FALSE;
|
||||
nsresult rv = SelectFile( parent,
|
||||
getter_AddRefs( outputFile ),
|
||||
|
@ -127,11 +127,7 @@ nsStreamTransfer::SelectFileAndTransferLocation( nsIChannel *aChannel,
|
|||
|
||||
if ( NS_SUCCEEDED( rv )
|
||||
&&
|
||||
outputFile
|
||||
&&
|
||||
NS_SUCCEEDED( outputFile->IsValid( &isValid ) )
|
||||
&&
|
||||
isValid ) {
|
||||
outputFile ) {
|
||||
// Try to get HTTP channel.
|
||||
nsCOMPtr<nsIHTTPChannel> httpChannel = do_QueryInterface( aChannel );
|
||||
if ( httpChannel ) {
|
||||
|
@ -218,7 +214,7 @@ nsStreamTransfer::SelectFileAndTransferLocationSpec( char const *aURL,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsStreamTransfer::SelectFile( nsIDOMWindowInternal *parent, nsIFileSpec **aResult, const nsCString &suggested ) {
|
||||
nsStreamTransfer::SelectFile( nsIDOMWindowInternal *parent, nsILocalFile **aResult, const nsCString &suggested ) {
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if ( aResult ) {
|
||||
|
@ -277,43 +273,19 @@ nsStreamTransfer::SelectFile( nsIDOMWindowInternal *parent, nsIFileSpec **aResul
|
|||
rv = picker->Show( &rc );
|
||||
}
|
||||
|
||||
#ifdef DEBUG_law
|
||||
printf( "\nFile picker result = 0x%04X\n\n", (int)rc );
|
||||
#endif
|
||||
if ( rc != nsIFilePicker::returnCancel ) {
|
||||
// Give result to caller.
|
||||
nsCOMPtr<nsILocalFile> selection;
|
||||
if ( NS_SUCCEEDED( picker->GetFile( getter_AddRefs( selection ) ) ) ) {
|
||||
nsXPIDLCString selectionPath;
|
||||
if ( NS_SUCCEEDED( selection->GetPath( getter_Copies( selectionPath ) ) ) ) {
|
||||
rv = NS_NewFileSpec( aResult );
|
||||
if ( NS_SUCCEEDED( rv ) ) {
|
||||
rv = (*aResult)->SetNativePath( selectionPath );
|
||||
printf( "\nresult native path = %s\n\n", (const char *)selectionPath );
|
||||
}
|
||||
}
|
||||
}
|
||||
rv = picker->GetFile( getter_AddRefs( aResult ) );
|
||||
|
||||
if ( NS_SUCCEEDED( rv ) && prefs ) {
|
||||
// Save selected directory for next time.
|
||||
nsCOMPtr<nsIFileSpec> startDirPath;
|
||||
rv = (*aResult)->GetParent( getter_AddRefs(startDirPath));
|
||||
nsCOMPtr<nsIFile> newStartDir;
|
||||
rv = (*aResult)->GetParent( getter_AddRefs( newStartDir ) );
|
||||
|
||||
// go through nsFileSpec to get to nsILocalFile
|
||||
nsFileSpec startDirSpec;
|
||||
startDir = do_QueryInterface( newStartDir );
|
||||
|
||||
if (NS_SUCCEEDED(rv))
|
||||
rv = startDirPath->GetFileSpec(&startDirSpec);
|
||||
|
||||
if (NS_SUCCEEDED(rv))
|
||||
rv = NS_FileSpecToIFile(&startDirSpec,
|
||||
getter_AddRefs(startDir));
|
||||
|
||||
if ( NS_SUCCEEDED( rv ) && startDir ) {
|
||||
prefs->SetFileXPref( "browser.download.dir", startDir);
|
||||
#ifdef DEBUG_law
|
||||
printf( "\nbrowser.download.dir has been reset\n\n" );
|
||||
#endif
|
||||
prefs->SetFileXPref( "browser.download.dir", startDir );
|
||||
}
|
||||
}
|
||||
} else if ( NS_SUCCEEDED( rv ) ) {
|
||||
|
|
|
@ -58,14 +58,15 @@ NS_IMPL_ISUPPORTS4(nsStreamXferOp, nsIStreamObserver, nsIStreamTransferOperation
|
|||
#endif
|
||||
|
||||
// ctor - save arguments in data members.
|
||||
nsStreamXferOp::nsStreamXferOp( nsIChannel *source, nsIFileSpec *target )
|
||||
nsStreamXferOp::nsStreamXferOp( nsIChannel *source, nsILocalFile *target )
|
||||
: mInputChannel( source ),
|
||||
mOutputChannel( 0 ),
|
||||
mOutputStream( 0 ),
|
||||
mOutputSpec( target ),
|
||||
mOutputFile( target ),
|
||||
mObserver( 0 ),
|
||||
mContentLength( 0 ),
|
||||
mBytesProcessed( 0 ) {
|
||||
mBytesProcessed( 0 ),
|
||||
mError( PR_FALSE ) {
|
||||
// Properly initialize refcnt.
|
||||
NS_INIT_REFCNT();
|
||||
}
|
||||
|
@ -73,9 +74,6 @@ nsStreamXferOp::nsStreamXferOp( nsIChannel *source, nsIFileSpec *target )
|
|||
// dtor
|
||||
nsStreamXferOp::~nsStreamXferOp() {
|
||||
// Delete dynamically allocated members (file and buffer).
|
||||
#ifdef DEBUG_law
|
||||
DEBUG_PRINTF( PR_STDOUT, "nsStreamXferOp destructor called\n" );
|
||||
#endif
|
||||
}
|
||||
|
||||
// Invoke nsIDOMWindowInternal::OpenDialog, passing this object as argument.
|
||||
|
@ -134,14 +132,24 @@ NS_IMETHODIMP
|
|||
nsStreamXferOp::OnError( int operation, nsresult errorCode ) {
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
#ifdef DEBUG_law
|
||||
DEBUG_PRINTF( PR_STDOUT, "nsStreamXferOp::OnError; op=%d, rv=0x%08X\n",
|
||||
operation, (int)errorCode );
|
||||
#endif
|
||||
// Record fact that an error has occurred.
|
||||
mError = PR_TRUE;
|
||||
|
||||
if ( mObserver ) {
|
||||
char buf[32];
|
||||
PR_snprintf( buf, sizeof( buf ), "%d %X", operation, (int)errorCode );
|
||||
// Pick off error codes of interest and convert to strings
|
||||
// so JS code can test for them without hardcoded numbers.
|
||||
PRUint32 reason = 0;
|
||||
if ( errorCode == NS_ERROR_FILE_ACCESS_DENIED ) {
|
||||
reason = kReasonAccessError;
|
||||
} else if ( errorCode == NS_ERROR_FILE_READ_ONLY ) {
|
||||
reason = kReasonAccessError;
|
||||
} else if ( errorCode == NS_ERROR_FILE_NO_DEVICE_SPACE ) {
|
||||
reason = kReasonDiskFull;
|
||||
} else if ( errorCode == NS_ERROR_FILE_DISK_FULL ) {
|
||||
reason = kReasonDiskFull;
|
||||
}
|
||||
char buf[64];
|
||||
PR_snprintf( buf, sizeof( buf ), "%d %X %u", operation, (int)errorCode, reason );
|
||||
rv = mObserver->Observe( (nsIStreamTransferOperation*)this,
|
||||
NS_ConvertASCIItoUCS2( NS_ISTREAMTRANSFER_CONTRACTID ";onError" ).GetUnicode(),
|
||||
NS_ConvertASCIItoUCS2( buf ).GetUnicode() );
|
||||
|
@ -167,14 +175,10 @@ nsStreamXferOp::Start( void ) {
|
|||
|
||||
if ( NS_SUCCEEDED( rv ) ) {
|
||||
// Next, create output file channel.
|
||||
nsFileSpec target; // XXX eliminate
|
||||
mOutputSpec->GetFileSpec( &target );
|
||||
nsCOMPtr<nsILocalFile> file;
|
||||
rv = NS_NewLocalFile(target, PR_FALSE, getter_AddRefs(file));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = fts->CreateTransport(file, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
|
||||
0664, getter_AddRefs( mOutputChannel));
|
||||
}
|
||||
rv = fts->CreateTransport( mOutputFile,
|
||||
PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
|
||||
0664,
|
||||
getter_AddRefs( mOutputChannel ) );
|
||||
|
||||
if ( NS_SUCCEEDED( rv ) ) {
|
||||
#ifdef USE_ASYNC_READ
|
||||
|
@ -272,21 +276,17 @@ nsStreamXferOp::Stop( void ) {
|
|||
// We also open the output stream at this point.
|
||||
NS_IMETHODIMP
|
||||
nsStreamXferOp::OnStartRequest(nsIChannel* channel, nsISupports* aContext) {
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
#ifdef DEBUG_law
|
||||
DEBUG_PRINTF( PR_STDOUT, "nsStreamXferOp::OnStartRequest; channel=0x%08X, context=0x%08X\n",
|
||||
(int)(void*)channel, (int)(void*)aContext );
|
||||
#endif
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
|
||||
#ifdef USE_ASYNC_READ
|
||||
// Open output stream.
|
||||
rv = mOutputChannel->OpenOutputStream( getter_AddRefs( mOutputStream ) );
|
||||
|
||||
if ( NS_FAILED( rv ) ) {
|
||||
// Give up all hope.
|
||||
this->OnError( kOpOpenOutputStream, rv );
|
||||
this->Stop();
|
||||
if ( !mOutputStream ) {
|
||||
// Open output stream.
|
||||
rv = mOutputChannel->OpenOutputStream( getter_AddRefs( mOutputStream ) );
|
||||
if ( NS_FAILED( rv ) ) {
|
||||
// Give up all hope.
|
||||
this->OnError( kOpOpenOutputStream, rv );
|
||||
this->Stop();
|
||||
}
|
||||
}
|
||||
#endif // USE_ASYNC_READ
|
||||
|
||||
|
@ -307,27 +307,42 @@ nsStreamXferOp::OnDataAvailable( nsIChannel *channel,
|
|||
// Write the data to the output stream.
|
||||
// Read a buffer full till aLength bytes have been processed.
|
||||
char buffer[ 8192 ];
|
||||
unsigned long bytesRemaining = aLength;
|
||||
while ( bytesRemaining ) {
|
||||
unsigned int bytesRead;
|
||||
char *bufPtr = buffer;
|
||||
unsigned long bytesToRead = aLength;
|
||||
while ( NS_SUCCEEDED( rv ) && bytesToRead ) {
|
||||
// Write out remainder of read buffer.
|
||||
unsigned int bytesToWrite;
|
||||
// Read a buffer full or the number remaining (whichever is smaller).
|
||||
rv = aIStream->Read( buffer,
|
||||
PR_MIN( sizeof( buffer ), bytesRemaining ),
|
||||
&bytesRead );
|
||||
PR_MIN( sizeof( buffer ), bytesToRead ),
|
||||
&bytesToWrite );
|
||||
if ( NS_SUCCEEDED( rv ) ) {
|
||||
// Write the bytes just read to the output stream.
|
||||
unsigned int bytesWritten;
|
||||
rv = mOutputStream->Write( buffer, bytesRead, &bytesWritten );
|
||||
if ( NS_SUCCEEDED( rv ) && bytesWritten == bytesRead ) {
|
||||
// All bytes written OK.
|
||||
bytesRemaining -= bytesWritten;
|
||||
} else {
|
||||
// Something is wrong.
|
||||
// Record bytes that have been read so far.
|
||||
bytesToRead -= bytesToWrite;
|
||||
|
||||
// Write out the data until something goes wrong, or, it is
|
||||
// all written. We loop because for some errors (e.g., disk
|
||||
// full), we get NS_OK with some bytes written, then an error.
|
||||
// So, we want to write again in that case to get the actual
|
||||
// error code.
|
||||
const char *bufPtr = buffer; // Where to write from.
|
||||
while ( NS_SUCCEEDED( rv ) && bytesToWrite ) {
|
||||
unsigned int bytesWritten = 0;
|
||||
rv = mOutputStream->Write( buffer, bytesToWrite, &bytesWritten );
|
||||
if ( NS_SUCCEEDED( rv ) ) {
|
||||
// Not all bytes were written for some strange reason.
|
||||
rv = NS_ERROR_FAILURE;
|
||||
// Adjust count of amount to write and the write location.
|
||||
bytesToWrite -= bytesWritten;
|
||||
bufPtr += bytesWritten;
|
||||
// Force an error if (for some reason) we get NS_OK but
|
||||
// no bytes written.
|
||||
if ( !bytesWritten ) {
|
||||
rv = NS_ERROR_FAILURE;
|
||||
this->OnError( kOpWrite, rv );
|
||||
}
|
||||
} else {
|
||||
// Something is wrong.
|
||||
this->OnError( kOpWrite, rv );
|
||||
}
|
||||
this->OnError( kOpWrite, rv );
|
||||
}
|
||||
} else {
|
||||
this->OnError( kOpRead, rv );
|
||||
|
@ -434,7 +449,7 @@ nsStreamXferOp::OnStopRequest( nsIChannel *channel,
|
|||
if ( NS_FAILED( aStatus ) ) {
|
||||
this->Stop();
|
||||
// XXX need to use aMsg when it is provided
|
||||
this->OnError( kOpAsyncWrite, aStatus );
|
||||
this->OnError( kOpAsyncRead, aStatus );
|
||||
}
|
||||
|
||||
// Close the output stream.
|
||||
|
@ -450,7 +465,7 @@ nsStreamXferOp::OnStopRequest( nsIChannel *channel,
|
|||
mOutputChannel = 0;
|
||||
|
||||
// Notify observer that the download is complete.
|
||||
if ( mObserver ) {
|
||||
if ( !mError && mObserver ) {
|
||||
nsString msg(aMsg);
|
||||
rv = mObserver->Observe( (nsIStreamTransferOperation*)this,
|
||||
NS_ConvertASCIItoUCS2( NS_ISTREAMTRANSFER_CONTRACTID ";onCompletion" ).GetUnicode(),
|
||||
|
@ -481,11 +496,11 @@ nsStreamXferOp::GetSource( nsIChannel**result ) {
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsStreamXferOp::GetTarget( nsIFileSpec**result ) {
|
||||
nsStreamXferOp::GetTarget( nsILocalFile**result ) {
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if ( result ) {
|
||||
*result = mOutputSpec;
|
||||
*result = mOutputFile;
|
||||
NS_IF_ADDREF( *result );
|
||||
} else {
|
||||
rv = NS_ERROR_NULL_POINTER;
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include "nsIStreamListener.h"
|
||||
#include "nsIChannel.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsIFileSpec.h"
|
||||
#include "nsILocalFile.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
|
@ -56,7 +56,7 @@ class nsStreamXferOp : public nsIStreamTransferOperation,
|
|||
#endif
|
||||
public:
|
||||
// ctor/dtor
|
||||
nsStreamXferOp( nsIChannel *source, nsIFileSpec *target );
|
||||
nsStreamXferOp( nsIChannel *source, nsILocalFile *target );
|
||||
virtual ~nsStreamXferOp();
|
||||
|
||||
// Implementation.
|
||||
|
@ -85,11 +85,12 @@ private:
|
|||
nsCOMPtr<nsIChannel> mInputChannel;
|
||||
nsCOMPtr<nsIChannel> mOutputChannel;
|
||||
nsCOMPtr<nsIOutputStream> mOutputStream;
|
||||
nsCOMPtr<nsIFileSpec> mOutputSpec;
|
||||
nsCOMPtr<nsILocalFile> mOutputFile;
|
||||
nsIObserver *mObserver; // Not owned; owner should call SetObserver(0) prior
|
||||
// to this object getting destroyed.
|
||||
int mContentLength;
|
||||
unsigned long mBytesProcessed;
|
||||
PRBool mError;
|
||||
}; // nsStreamXferOp
|
||||
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче