diff --git a/mozilla/widget/public/nsIFileSpecWithUI.idl b/mozilla/widget/public/nsIFileSpecWithUI.idl index 7befad9c986..22c250543f3 100644 --- a/mozilla/widget/public/nsIFileSpecWithUI.idl +++ b/mozilla/widget/public/nsIFileSpecWithUI.idl @@ -25,66 +25,48 @@ // may think they are. Welcome to reality. #include "nsIFileSpec.idl" - -%{C++ -#include "nscore.h" // for NS_WIDGET -#include "nsIComponentManager.h" -%} -native StandardFilterMask(nsIFileSpecWithUI::StandardFilterMask); +#include "nsIComponentManager.idl" [scriptable, uuid(8ddf7681-139a-11d3-915f-dc1f8c138b7c)] interface nsIFileSpecWithUI : nsIFileSpec { -%{C++ - // - // The "choose" functions present the file picker UI in order to set the - // value of the file spec. -%} + // Filter mask flags. These may be or'd together. + const unsigned long eAllReadable = 1<<0; + const unsigned long eHTMLFiles = 1<<1; + const unsigned long eXMLFiles = 1<<2; + const unsigned long eImageFiles = 1<<3; + const unsigned long eMailFiles = 1<<4; + const unsigned long eTextFiles = 1<<5; + const unsigned long eAllFiles = 1<<6; -%{C++ - // The mask for standard filters is given as follows: - enum StandardFilterMask - { - eAllReadable = (1<<0) - , eHTMLFiles = (1<<1) - , eXMLFiles = (1<<2) - , eImageFiles = (1<<3) - , eMailFiles = (1<<4) - , eTextFiles = (1<<5) - , eAllFiles = (1<<6) + const unsigned long eAllStandardFilters = eAllReadable | + eHTMLFiles | + eXMLFiles | + eImageFiles | + eMailFiles | + eTextFiles | + eAllFiles; + + const unsigned long eAllMailOutputFilters = eHTMLFiles | + eMailFiles | + eTextFiles; - // Mask containing all the above default filters - , eAllStandardFilters = ( - eAllReadable - | eHTMLFiles - | eXMLFiles - | eImageFiles - | eMailFiles - | eTextFiles - | eAllFiles) - , eAllMailOutputFilters = ( - eHTMLFiles - | eMailFiles - | eTextFiles) - - // The "extra filter" bit should be set if the "extra filter" - // is passed in to chooseInputFile. - , eExtraFilter = (1<<31) - }; - enum { kNumStandardFilters = 7, kNumMailFilters = 3 }; -%} - [noscript] void chooseInputFile( - in string title - , in StandardFilterMask standardFilterMask - , in string extraFilterTitle - , in string extraFilter - ); + const unsigned long eExtraFilter = 1<<31; - [noscript] void chooseOutputFile(in string windowTitle, - in string suggestedLeafName, - in StandardFilterMask standardFilterMask); + const unsigned long kNumStandardFilters = 7; + const unsigned long kNumMailFilters = 3; + + void chooseInputFile( in string title, + in unsigned long filterMask, + in string extraFilterTitle, + in string extraFilter ); + + void chooseOutputFile( in string windowTitle, + in string suggestedLeafName, + in unsigned long filterMask ); + + string chooseFile(in string title); - string chooseFile(in string title); string chooseDirectory(in string title); }; diff --git a/mozilla/widget/src/xpwidgets/nsFileSpecWithUIImpl.cpp b/mozilla/widget/src/xpwidgets/nsFileSpecWithUIImpl.cpp index ebc2ded5aec..ebddcea707a 100644 --- a/mozilla/widget/src/xpwidgets/nsFileSpecWithUIImpl.cpp +++ b/mozilla/widget/src/xpwidgets/nsFileSpecWithUIImpl.cpp @@ -83,7 +83,7 @@ nsFileSpecWithUIImpl::~nsFileSpecWithUIImpl() NS_IMETHODIMP nsFileSpecWithUIImpl::ChooseOutputFile( const char *windowTitle, const char *suggestedLeafName, - nsIFileSpecWithUI::StandardFilterMask outMask) + PRUint32 outMask) //---------------------------------------------------------------------------------------- { if (!mBaseFileSpec) @@ -101,10 +101,10 @@ NS_IMETHODIMP nsFileSpecWithUIImpl::ChooseOutputFile( SetFileWidgetFilterList(fileWidget, outMask, nsnull, nsnull); - nsFileSpec spec; // If there is a filespec specified, then start there. - if (GetFileSpec(&spec) != NS_ERROR_NOT_INITIALIZED) - fileWidget->SetDisplayDirectory(spec); + SetFileWidgetStartDir(fileWidget); + + nsFileSpec spec; nsString winTitle(windowTitle); @@ -129,7 +129,7 @@ NS_IMETHODIMP nsFileSpecWithUIImpl::ChooseFile(const char *title, char **_retval // --------------------------------------------------------------------------- void nsFileSpecWithUIImpl::SetFileWidgetFilterList( - nsIFileWidget* fileWidget, nsIFileSpecWithUI::StandardFilterMask mask, + nsIFileWidget* fileWidget, PRUint32 mask, const char *inExtraFilterTitle, const char *inExtraFilter) { if (!fileWidget) return; @@ -199,10 +199,24 @@ Clean: delete [] filters; } +//---------------------------------------------------------------------------------------- +void nsFileSpecWithUIImpl::SetFileWidgetStartDir( nsIFileWidget *fileWidget ) { + // We set the file widget's starting directory to the one specified by this nsIFileSpec. + if ( mBaseFileSpec && fileWidget ) { + nsFileSpec spec; + nsresult rv = mBaseFileSpec->GetFileSpec( &spec ); + if ( NS_SUCCEEDED( rv ) && spec.Valid() ) { + // Set as starting directory in file widget. + fileWidget->SetDisplayDirectory( spec ); + } + } +} + + //---------------------------------------------------------------------------------------- NS_IMETHODIMP nsFileSpecWithUIImpl::ChooseInputFile( const char *inTitle, - nsIFileSpecWithUI::StandardFilterMask inMask, + PRUint32 inMask, const char *inExtraFilterTitle, const char *inExtraFilter) //---------------------------------------------------------------------------------------- { @@ -220,6 +234,7 @@ NS_IMETHODIMP nsFileSpecWithUIImpl::ChooseInputFile( SetFileWidgetFilterList(fileWidget, inMask, inExtraFilterTitle, inExtraFilter); + SetFileWidgetStartDir(fileWidget); nsString winTitle(inTitle); if (fileWidget->GetFile(nsnull, winTitle, spec) != nsFileDlgResults_OK) rv = NS_FILE_FAILURE; @@ -243,6 +258,7 @@ NS_IMETHODIMP nsFileSpecWithUIImpl::ChooseDirectory(const char *title, char **_r (void**)getter_AddRefs(fileWidget)); if (NS_FAILED(rv)) return rv; + SetFileWidgetStartDir(fileWidget); nsFileSpec spec; if (fileWidget->GetFolder(nsnull, title, spec) != nsFileDlgResults_OK) rv = NS_FILE_FAILURE; diff --git a/mozilla/widget/src/xpwidgets/nsFileSpecWithUIImpl.h b/mozilla/widget/src/xpwidgets/nsFileSpecWithUIImpl.h index 1b369310355..f9b2587c295 100644 --- a/mozilla/widget/src/xpwidgets/nsFileSpecWithUIImpl.h +++ b/mozilla/widget/src/xpwidgets/nsFileSpecWithUIImpl.h @@ -103,7 +103,8 @@ class nsFileSpecWithUIImpl { return mBaseFileSpec ? mBaseFileSpec->GetParent(aParent) : NS_ERROR_NOT_INITIALIZED; } /* boolean equals(in nsIFileSpec spec); */ - NS_IMETHOD Equals(nsIFileSpec *spec, PRBool *result) { return mBaseFileSpec ? mBaseFileSpec->Equals(spec, result) : NS_ERROR_NOT_INITIALIZED; } + NS_IMETHOD Equals(nsIFileSpec *spec, PRBool *result) + { return mBaseFileSpec ? mBaseFileSpec->Equals(spec, result) : NS_ERROR_NOT_INITIALIZED; } /* nsIFileSpec makeUnique (); */ NS_IMETHOD MakeUnique() @@ -252,11 +253,13 @@ class nsFileSpecWithUIImpl // Data protected: - // helper - void SetFileWidgetFilterList(nsIFileWidget* fileWidget, - nsIFileSpecWithUI::StandardFilterMask mask, - const char *inExtraFilterTitle, - const char *inExtraFilter); + // helpers + void SetFileWidgetFilterList(nsIFileWidget* fileWidget, + PRUint32 mask, + const char *inExtraFilterTitle, + const char *inExtraFilter); + + void SetFileWidgetStartDir(nsIFileWidget* fileWidget); nsCOMPtr mBaseFileSpec; diff --git a/mozilla/xpfe/components/ucth/public/nsIUnkContentTypeHandler.idl b/mozilla/xpfe/components/ucth/public/nsIUnkContentTypeHandler.idl index 5962842d740..ce244803da9 100644 --- a/mozilla/xpfe/components/ucth/public/nsIUnkContentTypeHandler.idl +++ b/mozilla/xpfe/components/ucth/public/nsIUnkContentTypeHandler.idl @@ -22,14 +22,7 @@ #include "nsIAppShellComponent.idl" #include "domstubs.idl" - -interface nsIURI; -interface nsIChannel; - -%{C++ -class nsIDOMWindow; // Seems domstubs.idl should do this, though. -%} - +#include "nsIChannel.idl" /*----------------------- nsIUnknownContentTypeHandler ------------------------- | This file describes the interface for Mozilla's "unknown content-type | @@ -38,7 +31,7 @@ class nsIDOMWindow; // Seems domstubs.idl should do this, though. | | | This component provides one component-specific member function: | | HandleUnknownContentType. This method's areguments include: | -| o the URL at which the content resides | +| o the nsIChannel that encountered the content | | o the content-type | | o the window that encountered the unknown content. | | | diff --git a/mozilla/xpfe/components/ucth/resources/unknownContent.js b/mozilla/xpfe/components/ucth/resources/unknownContent.js index c969bb23aca..01f16797fb5 100644 --- a/mozilla/xpfe/components/ucth/resources/unknownContent.js +++ b/mozilla/xpfe/components/ucth/resources/unknownContent.js @@ -26,15 +26,17 @@ var dialog; function initData() { // Create data object and initialize. data = new Object; - data.location = window.arguments[0]; - data.contentType = window.arguments[1]; + data.channel = window.arguments[0]; + data.contentType = window.arguments[1]; + + // Get location from channel. + data.location = data.channel.URI.spec; } function initDialog() { // Create dialog object and initialize. dialog = new Object; dialog.contentType = document.getElementById("dialog.contentType"); -dump("dialog.contentType="+dialog.contentType+"\n"); dialog.more = document.getElementById("dialog.more"); dialog.pick = document.getElementById("dialog.pick"); dialog.save = document.getElementById("dialog.save"); @@ -58,7 +60,6 @@ function onLoad() { } function onUnload() { - // Nothing for now. } function more() { @@ -84,7 +85,7 @@ function save() { xfer.SelectFileAndTransferLocationSpec( data.location, window.opener ); } catch( exception ) { // Failed (or cancelled), give them another chance. - dump( "SelectFileAndTransferLocationSpec failed, rv=" + exception + "\n" ); + alert( "Save failed, rv=" + exception + "\n" ); return; } // Save underway, close this dialog. @@ -92,6 +93,6 @@ function save() { } function cancel() { - // Close the window. + // Close this dialog. window.close(); } diff --git a/mozilla/xpfe/components/ucth/src/nsUnknownContentTypeHandler.cpp b/mozilla/xpfe/components/ucth/src/nsUnknownContentTypeHandler.cpp index 1302725fe70..474887938e6 100644 --- a/mozilla/xpfe/components/ucth/src/nsUnknownContentTypeHandler.cpp +++ b/mozilla/xpfe/components/ucth/src/nsUnknownContentTypeHandler.cpp @@ -24,11 +24,10 @@ #include "nsIAppShellComponentImpl.h" #include "nsString.h" -#include "nsIURL.h" #include "nsIDOMWindow.h" #include "nsIScriptGlobalObject.h" - #include "nsIChannel.h" +#include "nsIURI.h" // {42770B50-03E9-11d3-8068-00600811A9C3} #define NS_UNKNOWNCONTENTTYPEHANDLER_CID \ @@ -64,79 +63,78 @@ NS_IMETHODIMP nsUnknownContentTypeHandler::HandleUnknownContentType( nsIChannel *aChannel, const char *aContentType, nsIDOMWindow *aWindow ) { - if ( !aWindow ) { - return NS_ERROR_NULL_POINTER; - } - nsresult rv = NS_OK; - // Open "Unknown content type" dialog. - // We pass in the url and the content type. - // Note that the "parent" browser window will be window.opener within the - // new dialog. + nsCOMPtr channel; - // Extract URI from channel. - nsCOMPtr channelUri = nsnull; - rv = aChannel->GetURI(getter_AddRefs(channelUri)); - if ( NS_FAILED( rv ) ) { - DEBUG_PRINTF( PR_STDOUT, "%s %d: GetURI failed, rv=0x%08X\n", - (char*)__FILE__, (int)__LINE__, (int)rv ); - return rv; + if ( aChannel ) { + // Need root nsISupports for later JS_PushArguments call. + channel = do_QueryInterface( aChannel ); + + // Cancel input channel now. + rv = aChannel->Cancel(); + if ( NS_FAILED( rv ) ) { + DEBUG_PRINTF( PR_STDOUT, "%s %d: Cancel failed, rv=0x%08X\n", + (char*)__FILE__, (int)__LINE__, (int)rv ); + } } - // url string non-const in this case. - char *urlStr = 0; - - // Get url string from channel. - channelUri->GetSpec( &urlStr ); - - // Get JS context from parent window. - nsCOMPtr sgo = do_QueryInterface( aWindow, &rv ); - if ( NS_SUCCEEDED( rv ) && sgo ) { - nsCOMPtr context; - sgo->GetContext( getter_AddRefs( context ) ); - if ( context ) { - JSContext *jsContext = (JSContext*)context->GetNativeContext(); - if ( jsContext ) { - void *stackPtr; - jsval *argv = JS_PushArguments( jsContext, - &stackPtr, - "sssss", - "chrome://global/content/unknownContent.xul", - "_blank", - "chrome", - urlStr, - aContentType ); - // Free url string. - nsCRT::free(urlStr); - if ( argv ) { - nsIDOMWindow *newWindow; - rv = aWindow->OpenDialog( jsContext, argv, 5, &newWindow ); - if ( NS_SUCCEEDED( rv ) ) { - newWindow->Release(); + if ( NS_SUCCEEDED( rv ) && channel && aContentType && aWindow ) { + // Open "Unknown content type" dialog. + // We pass in the channel and the content type. + // Note that the "parent" browser window will be window.opener within the + // new dialog. + + // Get JS context from parent window. + nsCOMPtr sgo = do_QueryInterface( aWindow, &rv ); + if ( NS_SUCCEEDED( rv ) && sgo ) { + nsCOMPtr context; + sgo->GetContext( getter_AddRefs( context ) ); + if ( context ) { + JSContext *jsContext = (JSContext*)context->GetNativeContext(); + if ( jsContext ) { + void *stackPtr; + jsval *argv = JS_PushArguments( jsContext, + &stackPtr, + "sss%ips", + "chrome://global/content/unknownContent.xul", + "_blank", + "chrome", + (const nsIID*)(&nsIChannel::GetIID()), + (nsISupports*)channel.get(), + aContentType ); + if ( argv ) { + nsCOMPtr newWindow; + rv = aWindow->OpenDialog( jsContext, argv, 5, getter_AddRefs( newWindow ) ); + if ( NS_FAILED( rv ) ) { + DEBUG_PRINTF( PR_STDOUT, "%s %d: OpenDialog failed, rv=0x%08X\n", + (char*)__FILE__, (int)__LINE__, (int)rv ); + } + JS_PopArguments( jsContext, stackPtr ); } else { - DEBUG_PRINTF( PR_STDOUT, "%s %d: OpenDialog failed, rv=0x%08X\n", - (char*)__FILE__, (int)__LINE__, (int)rv ); + DEBUG_PRINTF( PR_STDOUT, "%s %d: JS_PushArguments failed\n", + (char*)__FILE__, (int)__LINE__ ); + rv = NS_ERROR_FAILURE; } - JS_PopArguments( jsContext, stackPtr ); } else { - DEBUG_PRINTF( PR_STDOUT, "%s %d: JS_PushArguments failed\n", + DEBUG_PRINTF( PR_STDOUT, "%s %d: GetNativeContext failed\n", (char*)__FILE__, (int)__LINE__ ); rv = NS_ERROR_FAILURE; } } else { - DEBUG_PRINTF( PR_STDOUT, "%s %d: GetNativeContext failed\n", + DEBUG_PRINTF( PR_STDOUT, "%s %d: GetContext failed\n", (char*)__FILE__, (int)__LINE__ ); rv = NS_ERROR_FAILURE; } } else { - DEBUG_PRINTF( PR_STDOUT, "%s %d: GetContext failed\n", - (char*)__FILE__, (int)__LINE__ ); - rv = NS_ERROR_FAILURE; + DEBUG_PRINTF( PR_STDOUT, "%s %d: QueryInterface (for nsIScriptGlobalObject) failed, rv=0x%08X\n", + (char*)__FILE__, (int)__LINE__, (int)rv ); } } else { - DEBUG_PRINTF( PR_STDOUT, "%s %d: QueryInterface (for nsIScriptGlobalObject) failed, rv=0x%08X\n", - (char*)__FILE__, (int)__LINE__, (int)rv ); + // If no error recorded so far, set one now. + if ( NS_SUCCEEDED( rv ) ) { + rv = NS_ERROR_NULL_POINTER; + } } return rv; diff --git a/mozilla/xpfe/components/xfer/public/nsIStreamTransfer.idl b/mozilla/xpfe/components/xfer/public/nsIStreamTransfer.idl index 15f64ab0abb..acdd88e0b74 100644 --- a/mozilla/xpfe/components/xfer/public/nsIStreamTransfer.idl +++ b/mozilla/xpfe/components/xfer/public/nsIStreamTransfer.idl @@ -22,13 +22,7 @@ #include "nsIAppShellComponent.idl" #include "domstubs.idl" - -[ptr] native nsIURI( nsIURI ); - -%{C++ -class nsIURI; -class nsIDOMWindow; -%} +#include "nsIChannel.idl" /*----------------------------- nsIStreamTransfer ------------------------------ | This file describes Mozilla's general-purpose "stream transfer" component. | @@ -51,11 +45,18 @@ class nsIDOMWindow; interface nsIStreamTransfer : nsIAppShellComponent { /*-------------------- SelectFileAndTransferLocation ----------------------- - | Prompt the user for a destination file and then transfer the data using | - | the argument URL as source to that file, while showing a progress | + | Prompt the user for a destination file and then transfer the data, using | + | the argument channel as source, to that file, while showing a progress | + | dialog. | + --------------------------------------------------------------------------*/ + [noscript] void SelectFileAndTransferLocation( in nsIChannel aChannel, + in nsIDOMWindow parent ); + + /*------------------ SelectFileAndTransferLocationSpec --------------------- + | Prompt the user for a destination file and then transfer the data, using | + | the argument URL as source, to that file, while showing a progress | | dialog. | --------------------------------------------------------------------------*/ - [noscript] void SelectFileAndTransferLocation( in nsIURI aURL, in nsIDOMWindow parent ); void SelectFileAndTransferLocationSpec( in string aURL, in nsIDOMWindow parent ); }; diff --git a/mozilla/xpfe/components/xfer/public/nsIStreamTransferOperation.idl b/mozilla/xpfe/components/xfer/public/nsIStreamTransferOperation.idl index bda60d5cf75..08d71492671 100644 --- a/mozilla/xpfe/components/xfer/public/nsIStreamTransferOperation.idl +++ b/mozilla/xpfe/components/xfer/public/nsIStreamTransferOperation.idl @@ -21,6 +21,8 @@ */ #include "nsISupports.idl" +#include "nsIChannel.idl" +#include "nsIFileSpec.idl" interface nsIObserver; @@ -34,11 +36,22 @@ interface nsIObserver; ------------------------------------------------------------------------------*/ [scriptable, uuid(E2200F90-3E23-11d3-806A-00600811A9C3)] interface nsIStreamTransferOperation : nsISupports { - readonly attribute string source; - readonly attribute string target; + readonly attribute nsIChannel source; + readonly attribute nsIFileSpec target; attribute nsIObserver observer; void Start(); void Stop(); + + // Operation codes (for error notifications): + const unsigned long kOpAsyncRead = 1; + const unsigned long kOpWrite = 2; + const unsigned long kOpOpenOutputStream = 3; + const unsigned long kOpCreateTransport = 4; + const unsigned long kOpGetService = 5; + const unsigned long kOpInputCancel = 6; + const unsigned long kOpOutputClose = 8; + const unsigned long kOpOutputCancel = 9; + const unsigned long kOpRead = 10; }; diff --git a/mozilla/xpfe/components/xfer/resources/downloadProgress.js b/mozilla/xpfe/components/xfer/resources/downloadProgress.js index e03b406e030..fa19e44c4de 100644 --- a/mozilla/xpfe/components/xfer/resources/downloadProgress.js +++ b/mozilla/xpfe/components/xfer/resources/downloadProgress.js @@ -1,9 +1,32 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, + * released March 31, 1998. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + * + * Contributors: + * William A. ("PowerGUI") Law + */ var data; // nsIStreamTransferOperation object var dialog; function loadDialog() { - dialog.location.setAttribute( "value", data.source ); - dialog.fileName.setAttribute( "value", data.target ); + dialog.location.setAttribute( "value", data.source.URI.spec ); + dialog.fileName.setAttribute( "value", data.target.nativePath ); } var progId = "component://netscape/appshell/component/xfer"; @@ -21,7 +44,7 @@ var observer = { onCompletion( data ); break; default: - dump( "Unknown topic: " + topic + "\n" ); + alert( "Unknown topic: " + topic + "\nData: " + data ); break; } return; @@ -59,8 +82,11 @@ function onUnload() { // Unhook observer. data.observer = null; - // Terminate transfer. - data.Stop(); + // See if we completed normally (i.e., are closing ourself). + if ( !completed ) { + // Terminate transfer. + data.Stop(); + } } var started = false; @@ -84,14 +110,16 @@ function onProgress( bytes, max ) { // Initialize download start time. started = true; startTime = ( new Date() ).getTime(); - // Let the user stop, now. - dialog.cancel.removeAttribute( "disabled" ); } // Get current time. var now = ( new Date() ).getTime(); // If interval hasn't elapsed, ignore it. - if ( now - lastUpdate < interval && eval(bytes) < eval(max) ) { + if ( now - lastUpdate < interval + && + max != "-1" + && + eval(bytes) < eval(max) ) { return; } @@ -108,19 +136,31 @@ function onProgress( bytes, max ) { } // Calculate percentage. - var percent = Math.round( (bytes*100)/max ); + var percent; + if ( max != "-1" ) { + percent = Math.round( (bytes*100)/max ); + + // Advance progress meter. + dialog.progress.setAttribute( "value", percent ); + } else { + percent = "??"; + + // Progress meter should be barber-pole in this case. + dialog.progress.setAttribute( "mode", "undetermined" ); + } - // Advance progress meter. - dialog.progress.setAttribute( "value", percent ); - // Check if download complete. if ( !completed ) { // Update status (nnn of mmm) var status = "( "; status += Math.round( bytes/1024 ); status += "K of "; - status += Math.round( max/1024 ); - status += "K bytes "; + if ( max != "-1" ) { + status += Math.round( max/1024 ); + status += "K bytes "; + } else { + status += "??.?K bytes "; + } if ( rate ) { status += "at "; status += Math.round( (rate*10)/1024 ) / 10; @@ -137,9 +177,11 @@ function onProgress( bytes, max ) { if ( !completed ) { // Update time remaining. - if ( rate ) { + if ( rate && max != "-1" ) { var rem = Math.round( ( max - bytes ) / rate ); // In seconds. dialog.timeLeft.childNodes[0].nodeValue = formatSeconds( rem ); + } else { + dialog.timeLeft.childNodes[0].nodeValue = "??:??:??"; } } else { // Clear time remaining field. @@ -150,12 +192,12 @@ function onProgress( bytes, max ) { function formatSeconds( nSecs ) { status = ""; if ( nSecs >= 3600 ) { - status += Math.round( nSecs/3600 ) + " hours, "; + status += Math.round( nSecs/3600 ) + ":"; nSecs = nSecs % 3600; } - status += Math.round( nSecs/60 ) + " minutes and "; + status += Math.round( nSecs/60 ) + ":"; nSecs = nSecs % 60; - status += nSecs + " seconds"; + status += nSecs; return status; } @@ -164,8 +206,22 @@ function onCompletion( status ) { completed = true; // Indicate completion in status area. onStatus( "Download completed in " + formatSeconds( elapsed/1000 ) ); - // Close the window in 2 seconds (to ensure user sees we're done). - window.setTimeout( "window.close();", 2000 ); + // Put progress meter at 100%. + dialog.progress.setAttribute( "value", 100 ); + dialog.progress.setAttribute( "mode", "normal" ); + try { + // 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" ); + for ( prop in exception ) { + dump( "exception." + prop + "=" + exception[ prop ] + "\n" ); + } + // Bug prevents that from working, just close the window. + window.close(); + // If that's not working either, change button text to give user a clue. + dialog.cancel.childNodes[0].nodeValue = "Close"; + } } function onStatus( status ) { diff --git a/mozilla/xpfe/components/xfer/src/nsStreamTransfer.cpp b/mozilla/xpfe/components/xfer/src/nsStreamTransfer.cpp index c629362ed23..2886946b735 100644 --- a/mozilla/xpfe/components/xfer/src/nsStreamTransfer.cpp +++ b/mozilla/xpfe/components/xfer/src/nsStreamTransfer.cpp @@ -23,18 +23,15 @@ #include "nsIAppShellComponentImpl.h" #include "nsStreamXferOp.h" -#include "nsIFileWidget.h" -#include "nsWidgetsCID.h" -#include "nsIURL.h" +#include "nsIFileSpecWithUI.h" #include "nsNeckoUtil.h" +#include "nsIPref.h" +#include "nsIURL.h" // {BEBA91C0-070F-11d3-8068-00600811A9C3} #define NS_STREAMTRANSFER_CID \ { 0xbeba91c0, 0x70f, 0x11d3, { 0x80, 0x68, 0x0, 0x60, 0x8, 0x11, 0xa9, 0xc3 } } -static NS_DEFINE_IID( kCFileWidgetCID, NS_FILEWIDGET_CID ); -static NS_DEFINE_IID( kIFileWidgetIID, NS_IFILEWIDGET_IID ); - // Implementation of the stream transfer component interface. class nsStreamTransfer : public nsIStreamTransfer, public nsAppShellComponentImpl { @@ -59,31 +56,36 @@ public: private: // Put up file picker dialog. - NS_IMETHOD SelectFile( nsFileSpec &result ); + NS_IMETHOD SelectFile( nsIFileSpec **result, const nsCString &suggested ); + nsCString SuggestNameFor( nsIChannel *aChannel ); // Objects of this class are counted to manage library unloading... nsInstanceCounter instanceCounter; }; // nsStreamTransfer NS_IMETHODIMP -nsStreamTransfer::SelectFileAndTransferLocation( nsIURI *aURL, nsIDOMWindow *parent ) { +nsStreamTransfer::SelectFileAndTransferLocation( nsIChannel *aChannel, nsIDOMWindow *parent ) { // Prompt the user for the destination file. - nsFileSpec outputFileName; - nsresult rv = SelectFile( outputFileName ); + nsCOMPtr outputFile; + PRBool isValid = PR_FALSE; + nsresult rv = SelectFile( getter_AddRefs( outputFile ), + SuggestNameFor( aChannel ).GetBuffer() ); - if ( NS_SUCCEEDED( rv ) ) { - // Open a downloadProgress dialog. - char *source = 0; - aURL->GetSpec( &source ); + if ( NS_SUCCEEDED( rv ) + && + outputFile + && + NS_SUCCEEDED( outputFile->IsValid( &isValid ) ) + && + isValid ) { + // Construct stream transfer operation to be given to dialog. + nsStreamXferOp *p= new nsStreamXferOp( aChannel, outputFile ); - nsStreamXferOp *p= new nsStreamXferOp( source, (const char*)outputFileName ); - nsCOMPtr op = dont_QueryInterface( (nsIStreamTransferOperation*)p ); - - nsCRT::free( source ); - - if ( op ) { + if ( p ) { // Open download progress dialog. + NS_ADDREF(p); rv = p->OpenDialog( parent ); + NS_RELEASE(p); if ( NS_FAILED( rv ) ) { DEBUG_PRINTF( PR_STDOUT, "%s %d : Error opening dialog, rv=0x%08X\n", (char *)__FILE__, (int)__LINE__, (int)rv ); @@ -94,7 +96,11 @@ nsStreamTransfer::SelectFileAndTransferLocation( nsIURI *aURL, nsIDOMWindow *par rv = NS_ERROR_OUT_OF_MEMORY; } } else { - DEBUG_PRINTF( PR_STDOUT, "Failed to select file, rv=0x%X\n", (int)rv ); + if ( NS_FAILED( rv ) ) { + DEBUG_PRINTF( PR_STDOUT, "Failed to select file, rv=0x%X\n", (int)rv ); + } else { + // User cancelled. + } } return rv; @@ -105,45 +111,107 @@ nsStreamTransfer::SelectFileAndTransferLocationSpec( char const *aURL, nsIDOMWin nsresult rv = NS_OK; // Construct URI from spec. - nsIURI *uri; - rv = NS_NewURI( &uri, aURL ); + nsCOMPtr uri; + rv = NS_NewURI( getter_AddRefs( uri ), aURL ); if ( NS_SUCCEEDED( rv ) && uri ) { - rv = this->SelectFileAndTransferLocation( uri,parent ); - NS_RELEASE( uri ); + // Construct channel from URI. + nsCOMPtr channel; + rv = NS_OpenURI( getter_AddRefs( channel ), uri, nsnull ); + + if ( NS_SUCCEEDED( rv ) && channel ) { + // Transfer channel to output file chosen by user. + rv = this->SelectFileAndTransferLocation( channel, parent ); + } else { + DEBUG_PRINTF( PR_STDOUT, "Failed to open URI, rv=0x%X\n", (int)rv ); + } + } else { + DEBUG_PRINTF( PR_STDOUT, "Failed to create URI, rv=0x%X\n", (int)rv ); } return rv; } NS_IMETHODIMP -nsStreamTransfer::SelectFile( nsFileSpec &aResult ) { +nsStreamTransfer::SelectFile( nsIFileSpec **aResult, const nsCString &suggested ) { nsresult rv = NS_OK; - // Prompt user for file name. - nsIFileWidget* fileWidget; - - nsString title("Save File"); + if ( aResult ) { + *aResult = 0; - rv = nsComponentManager::CreateInstance( kCFileWidgetCID, - nsnull, - kIFileWidgetIID, - (void**)&fileWidget ); - - if ( NS_SUCCEEDED( rv ) && fileWidget ) { - nsFileDlgResults result = fileWidget->PutFile( nsnull, title, aResult ); - if ( result == nsFileDlgResults_OK || result == nsFileDlgResults_Replace ) { + // Prompt user for file name. + nsCOMPtr result; + result = getter_AddRefs( NS_CreateFileSpecWithUI() ); + + if ( result ) { + // Prompt for file name. + nsCOMPtr startDir; + + // Pull in the user's preferences and get the default download directory. + NS_WITH_SERVICE( nsIPref, prefs, NS_PREF_PROGID, &rv ); + if ( NS_SUCCEEDED( rv ) && prefs ) { + prefs->GetFilePref( "browser.download.dir", getter_AddRefs( startDir ) ); + if ( startDir ) { + PRBool isValid = PR_FALSE; + startDir->IsValid( &isValid ); + if ( isValid ) { + // Set result so startDir is used. + result->FromFileSpec( startDir ); + } + } + } + + //XXX l10n + nsAutoCString title("Save File"); + + rv = result->ChooseOutputFile( title, + suggested.IsEmpty() ? 0 : suggested.GetBuffer(), + nsIFileSpecWithUI::eAllFiles ); + + if ( NS_SUCCEEDED( rv ) ) { + // Give result to caller. + rv = result->QueryInterface( nsIFileSpec::GetIID(), (void**)aResult ); + + if ( NS_SUCCEEDED( rv ) && prefs ) { + // Save selected directory for next time. + rv = result->GetParent( getter_AddRefs( startDir ) ); + if ( NS_SUCCEEDED( rv ) && startDir ) { + prefs->SetFilePref( "browser.download.dir", startDir, PR_FALSE ); + } + } + } } else { - rv = NS_ERROR_ABORT; + DEBUG_PRINTF( PR_STDOUT, "%s %d: Error creating file widget, rv=0x%X\n", + __FILE__, (int)__LINE__, (int)rv ); } - NS_RELEASE( fileWidget ); } else { - DEBUG_PRINTF( PR_STDOUT, "%s %d: Error creating file widget, rv=0x%X\n", - __FILE__, (int)__LINE__, (int)rv ); + rv = NS_ERROR_NULL_POINTER; } return rv; } +nsCString nsStreamTransfer::SuggestNameFor( nsIChannel *aChannel ) { + nsCString result; + if ( aChannel ) { + // Get URI from channel and spec from URI. + nsCOMPtr uri; + nsresult rv = aChannel->GetURI( getter_AddRefs( uri ) ); + if ( NS_SUCCEEDED( rv ) && uri ) { + // Try to get URL from URI. + nsCOMPtr url( do_QueryInterface( uri, &rv ) ); + if ( NS_SUCCEEDED( rv ) && url ) { + char *nameFromURL = 0; + rv = url->GetFileName( &nameFromURL ); + if ( NS_SUCCEEDED( rv ) && nameFromURL ) { + result = nameFromURL; + nsCRT::free( nameFromURL ); + } + } + } + } + return result; +} + // Generate base nsIAppShellComponent implementation. NS_IMPL_IAPPSHELLCOMPONENT( nsStreamTransfer, nsIStreamTransfer, diff --git a/mozilla/xpfe/components/xfer/src/nsStreamXferOp.cpp b/mozilla/xpfe/components/xfer/src/nsStreamXferOp.cpp index c0918ba2748..42f82b11101 100644 --- a/mozilla/xpfe/components/xfer/src/nsStreamXferOp.cpp +++ b/mozilla/xpfe/components/xfer/src/nsStreamXferOp.cpp @@ -20,39 +20,40 @@ * Contributor(s): */ #include "nsStreamXferOp.h" - #include "nsIStreamTransfer.h" -#include "nsString.h" -#include "nsCOMPtr.h" -#include "nsFileStream.h" -#include "nsFileSpec.h" + +// Basic dependencies. +#include "nsIServiceManager.h" + +// For notifying observer. #include "nsIObserver.h" + +// For opening dialog. #include "nsIDOMWindow.h" #include "nsIScriptGlobalObject.h" -#include "nsNeckoUtil.h" -#include "nsIURL.h" -#include "nsIChannel.h" -#include "nsIEventQueueService.h" -#include "nsIBufferInputStream.h" -static NS_DEFINE_IID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); #include "jsapi.h" -#include "prprf.h" + +// For opening input/output streams. +#include "nsIFileTransportService.h" +#include "nsIOutputStream.h" +#include "nsNeckoUtil.h" #ifdef NS_DEBUG +#include "prprf.h" #define DEBUG_PRINTF PR_fprintf #else #define DEBUG_PRINTF (void) #endif // ctor - save arguments in data members. -nsStreamXferOp::nsStreamXferOp( const nsString &source, const nsString &target ) - : mSource( source ), - mTarget( target ), +nsStreamXferOp::nsStreamXferOp( nsIChannel *source, nsIFileSpec *target ) + : mInputChannel( source ), + mOutputChannel( 0 ), + mOutputStream( 0 ), + mOutputSpec( target ), mObserver( 0 ), - mBufLen( 8192 ), - mBuffer( new char[ mBufLen ] ), - mStopped( PR_FALSE ), - mOutput( 0 ) { + mContentLength( 0 ), + mBytesProcessed( 0 ) { // Properly initialize refcnt. NS_INIT_REFCNT(); } @@ -60,9 +61,9 @@ nsStreamXferOp::nsStreamXferOp( const nsString &source, const nsString &target ) // dtor nsStreamXferOp::~nsStreamXferOp() { // Delete dynamically allocated members (file and buffer). +#ifdef DEBUG_law DEBUG_PRINTF( PR_STDOUT, "nsStreamXferOp destructor called\n" ); - delete mOutput; - delete [] mBuffer; +#endif } // Invoke nsIDOMWindow::OpenDialog, passing this object as argument. @@ -87,11 +88,9 @@ nsStreamXferOp::OpenDialog( nsIDOMWindow *parent ) { (const nsIID*)(&nsCOMTypeInfo::GetIID()), (nsISupports*)(nsIStreamTransferOperation*)this ); if ( argv ) { - nsIDOMWindow *newWindow; - rv = parent->OpenDialog( jsContext, argv, 4, &newWindow ); - if ( NS_SUCCEEDED( rv ) ) { - newWindow->Release(); - } else { + nsCOMPtr newWindow; + rv = parent->OpenDialog( jsContext, argv, 4, getter_AddRefs( newWindow ) ); + if ( NS_FAILED( rv ) ) { DEBUG_PRINTF( PR_STDOUT, "%s %d: nsIDOMWindow::OpenDialog failed, rv=0x%08X\n", (char*)__FILE__, (int)__LINE__, (int)rv ); } @@ -118,64 +117,118 @@ nsStreamXferOp::OpenDialog( nsIDOMWindow *parent ) { return rv; } -// Start the download by opening the output file and then loading the source location. +// Notify observer of error. +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 + + if ( mObserver ) { + char buf[32]; + PR_snprintf( buf, sizeof( buf ), "%d %X", operation, (int)errorCode ); + rv = mObserver->Observe( (nsIStreamTransferOperation*)this, + nsAutoString( NS_ISTREAMTRANSFER_PROGID ";onError" ).GetUnicode(), + nsAutoString( buf ).GetUnicode() ); + if ( NS_FAILED( rv ) ) { + DEBUG_PRINTF( PR_STDOUT, "%s %d: Observe failed, rv=0x%08X\n", + (char*)__FILE__, (int)__LINE__, (int)rv ); + } + } + + return rv; +}; + +// Start the download by opening the output file and then reading the input channel. NS_IMETHODIMP nsStreamXferOp::Start( void ) { nsresult rv = NS_OK; - if ( !mOutput ) { - // Open the output file stream. - mOutput = new nsOutputFileStream( nsFileSpec( mTarget.GetBuffer() ) ); - if ( mOutput ) { - if ( !mOutput->failed() ) { - nsIURI *url = 0; - rv = NS_NewURI( &url, mSource.GetBuffer() ); - if ( NS_SUCCEEDED( rv ) && url ) { - // XXX: Should there be a LoadGroup? - nsresult rv = NS_OpenURI( this, nsnull, url, nsnull - ); - NS_RELEASE(url); - + if ( mInputChannel ) { + if ( !mOutputChannel ) { + // First, get file transport service. + NS_DEFINE_IID(kFileTransportServiceCID, NS_FILETRANSPORTSERVICE_CID); + NS_WITH_SERVICE( nsIFileTransportService, fts, kFileTransportServiceCID, &rv ); + + if ( NS_SUCCEEDED( rv ) ) { + // Next, create output file channel. + nsFileSpec target; + mOutputSpec->GetFileSpec( &target ); + rv = fts->CreateTransport( target, + "load", + 0, + getter_AddRefs( mOutputChannel ) ); + + if ( NS_SUCCEEDED( rv ) ) { + // Read the input channel (with ourself as the listener). + rv = mInputChannel->AsyncRead( 0, -1, 0, this ); if ( NS_FAILED( rv ) ) { - DEBUG_PRINTF( PR_STDOUT, "%s %d: NS_OpenURI failed, rv=0x%08X\n", - (char*)__FILE__, (int)__LINE__, (int)rv ); + this->OnError( kOpAsyncRead, rv ); } } else { - DEBUG_PRINTF( PR_STDOUT, "%s %d: NS_NewURI failed, rv=0x%X\n", - __FILE__, (int)__LINE__, (int)rv ); + this->OnError( kOpCreateTransport, rv ); + rv = NS_ERROR_OUT_OF_MEMORY; } - } else { - DEBUG_PRINTF( PR_STDOUT, "%s %d: error opening output file, rv=0x%08X\n", - (char*)__FILE__, (int)__LINE__, (int)mOutput->error() ); - delete mOutput; - mOutput = 0; + this->OnError( kOpGetService, rv ); } } else { - rv = NS_ERROR_OUT_OF_MEMORY; + rv = NS_ERROR_ALREADY_INITIALIZED; + this->OnError( 0, rv ); } - } else { - DEBUG_PRINTF( PR_STDOUT, "%s %d: nsStreamXferOp already started\n", - (char*)__FILE__, (int)__LINE__ ); - rv = NS_ERROR_ALREADY_INITIALIZED; + rv = NS_ERROR_NOT_INITIALIZED; + this->OnError( 0, rv ); + } + + // If an error occurred, shut down. + if ( NS_FAILED( rv ) ) { + this->Stop(); } return rv; } -// Terminate the download by setting flag (checked in OnDataAvailable). +// Terminate the download by cancelling/closing input and output channels. NS_IMETHODIMP nsStreamXferOp::Stop( void ) { nsresult rv = NS_OK; - // Set flag indicating netlib xfer should cease. - mStopped = PR_TRUE; + // Cancel input. + if ( mInputChannel ) { + // Unhook it first. + nsCOMPtr channel = mInputChannel; + mInputChannel = 0; + // Now cancel it. + rv = channel->Cancel(); + if ( NS_FAILED( rv ) ) { + this->OnError( kOpInputCancel, rv ); + } + } + + // Close output stream. + if ( mOutputStream ) { + // Unhook it first. + nsCOMPtr stream = mOutputStream; + mOutputStream = 0; + + // Now close it. + rv = stream->Close(); + if ( NS_FAILED( rv ) ) { + this->OnError( kOpOutputClose, rv ); + } + } + + // Cancel output channel. + mOutputChannel = 0; return rv; } -// Process the data by reading it and then writing it to the output file. +// Process the data by writing it to the output channel. NS_IMETHODIMP nsStreamXferOp::OnDataAvailable( nsIChannel *channel, nsISupports *aContext, @@ -184,67 +237,102 @@ nsStreamXferOp::OnDataAvailable( nsIChannel *channel, PRUint32 aLength ) { nsresult rv = NS_OK; - // Check for download cancelled by user. - if ( mStopped ) { - // Close the output file. - if ( mOutput ) { - mOutput->close(); - } - // Close the input stream. - aIStream->Close(); - } else { - // Allocate buffer space. - if ( aLength > mBufLen ) { - char *oldBuffer = mBuffer; - - mBuffer = new char[ aLength ]; - - if ( mBuffer ) { - // Use new (bigger) buffer. - mBufLen = aLength; - // Delete old (smaller) buffer. - delete [] oldBuffer; - } else { - // Keep the one we've got. - mBuffer = oldBuffer; - } - } - - // Read the data. - PRUint32 bytesRead; - rv = aIStream->Read( mBuffer, ( mBufLen > aLength ) ? aLength : mBufLen, &bytesRead ); - - if ( NS_SUCCEEDED(rv) && bytesRead > 0 ) { - // Write the data just read to the output stream. - if ( mOutput ) { - mOutput->write( mBuffer, bytesRead ); - if ( mOutput->failed() ) { - DEBUG_PRINTF( PR_STDOUT, "%s %d: Error writing file, rv=0x%08X\n", - (char*)__FILE__, (int)__LINE__, (int)mOutput->error() ); +#ifdef DEBUG_law + DEBUG_PRINTF( PR_STDOUT, "nsStreamXferOp::OnDataAvailable, offset=%d length=%d\n", + (int)offset, (int)aLength ); +#endif + + if ( mOutputStream ) { + // 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; + // Read a buffer full or the number remaining (whichever is smaller). + rv = aIStream->Read( buffer, + PR_MIN( sizeof( buffer ), + bytesRemaining ), + &bytesRead ); + 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. + if ( NS_SUCCEEDED( rv ) ) { + // Not all bytes were written for some strange reason. + rv = NS_ERROR_FAILURE; + } + this->OnError( kOpWrite, rv ); } + } else { + this->OnError( kOpRead, rv ); } - } else { - DEBUG_PRINTF( PR_STDOUT, "%s %d: Error reading stream, rv=0x%08X\n", - (char*)__FILE__, (int)__LINE__, (int)rv ); } + } else { + rv = NS_ERROR_NOT_INITIALIZED; + this->OnError( 0, rv ); + } + + if ( NS_FAILED( rv ) ) { + // Oh dear. close up shop. + this->Stop(); + } else { + // Fake OnProgress. + mBytesProcessed += aLength; + if ( mContentLength == 0 && channel ) { + // Get content length from input channel. + channel->GetContentLength( &mContentLength ); + } + this->OnProgress( mOutputChannel, 0, mBytesProcessed, mContentLength ); } return rv; } -// We aren't interested in this notification; simply return NS_OK. +// This is called when the input channel is successfully opened. +// +// 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 + + // Open output stream. + rv = mOutputChannel->OpenOutputStream( 0, getter_AddRefs( mOutputStream ) ); + + if ( NS_FAILED( rv ) ) { + // Give up all hope. + this->OnError( kOpOpenOutputStream, rv ); + this->Stop(); + } + return rv; } + +// As an event sink getter, we get ourself. +NS_IMETHODIMP +nsStreamXferOp::GetEventSink( const char *cmd, const nsIID &anIID, nsISupports **aResult ) { + return this->QueryInterface( anIID, (void**)aResult ); +} + // Pass notification to our observer (if we have one). This object is the // "subject", the topic is the component progid (plus ";onProgress"), and // the data is the progress numbers (in the form "%lu %lu" where the first // value is the number of bytes processed, the second the total number // expected. +// +//XXX This function is not called at the moment because this object is not +// provided as the event sink by any event sink getter! NS_IMETHODIMP nsStreamXferOp::OnProgress(nsIChannel* channel, nsISupports* aContext, PRUint32 aProgress, PRUint32 aProgressMax) { @@ -252,7 +340,7 @@ nsStreamXferOp::OnProgress(nsIChannel* channel, nsISupports* aContext, if ( mObserver ) { char buf[32]; - PR_snprintf( buf, sizeof buf, "%lu %lu", (unsigned long)aProgress, (unsigned long)aProgressMax ); + PR_snprintf( buf, sizeof buf, "%lu %ld", (unsigned long)aProgress, (long)aProgressMax ); rv = mObserver->Observe( (nsIStreamTransferOperation*)this, nsString( NS_ISTREAMTRANSFER_PROGID ";onProgress" ).GetUnicode(), nsString( buf ).GetUnicode() ); @@ -288,8 +376,7 @@ nsStreamXferOp::OnStatus( nsIChannel *channel, return rv; } -// Close the output stream. In addition, notify our observer -// (if we have one). +// This is called when the end of input is reached on the input channel. NS_IMETHODIMP nsStreamXferOp::OnStopRequest( nsIChannel *channel, nsISupports *aContext, @@ -297,12 +384,24 @@ nsStreamXferOp::OnStopRequest( nsIChannel *channel, const PRUnichar *aMsg ) { nsresult rv = NS_OK; - // Close the output file. - if ( mOutput ) { - mOutput->close(); +#ifdef DEBUG_law + DEBUG_PRINTF( PR_STDOUT, "nsStreamXferOp::OnStopRequest notified of input completion, status=0x%08X\n", + (int)aStatus ); +#endif + + // Close the output stream. + if ( mOutputStream ) { + rv = mOutputStream->Close(); + if ( NS_FAILED( rv ) ) { + this->OnError( kOpOutputClose, rv ); + } } - // Notify observer. + // Unhook input/output channels (don't need to cancel 'em). + mInputChannel = 0; + mOutputChannel = 0; + + // Notify observer that the download is complete. if ( mObserver ) { nsString msg = aMsg; rv = mObserver->Observe( (nsIStreamTransferOperation*)this, @@ -320,14 +419,12 @@ nsStreamXferOp::OnStopRequest( nsIChannel *channel, // Attribute getters/setters... NS_IMETHODIMP -nsStreamXferOp::GetSource( char**result ) { +nsStreamXferOp::GetSource( nsIChannel**result ) { nsresult rv = NS_OK; if ( result ) { - *result = mSource.ToNewCString(); - if ( !*result ) { - rv = NS_ERROR_OUT_OF_MEMORY; - } + *result = mInputChannel; + NS_IF_ADDREF( *result ); } else { rv = NS_ERROR_NULL_POINTER; } @@ -336,14 +433,12 @@ nsStreamXferOp::GetSource( char**result ) { } NS_IMETHODIMP -nsStreamXferOp::GetTarget( char**result ) { +nsStreamXferOp::GetTarget( nsIFileSpec**result ) { nsresult rv = NS_OK; if ( result ) { - *result = mTarget.ToNewCString(); - if ( !*result ) { - rv = NS_ERROR_OUT_OF_MEMORY; - } + *result = mOutputSpec; + NS_IF_ADDREF( *result ); } else { rv = NS_ERROR_NULL_POINTER; } @@ -390,11 +485,6 @@ nsStreamXferOp::QueryInterface( REFNSIID aIID, void** aInstancePtr ) { // Always NULL result, in case of failure *aInstancePtr = NULL; - if (aIID.Equals(nsCOMTypeInfo::GetIID())) { - *aInstancePtr = (void*) ((nsIProgressEventSink*)this); - NS_ADDREF_THIS(); - return NS_OK; - } if (aIID.Equals(nsCOMTypeInfo::GetIID())) { *aInstancePtr = (void*) ((nsIStreamObserver*)this); NS_ADDREF_THIS(); @@ -415,6 +505,16 @@ nsStreamXferOp::QueryInterface( REFNSIID aIID, void** aInstancePtr ) { NS_ADDREF_THIS(); return NS_OK; } + if (aIID.Equals(nsCOMTypeInfo::GetIID())) { + *aInstancePtr = (void*) ((nsIProgressEventSink*)this); + NS_ADDREF_THIS(); + return NS_OK; + } + if (aIID.Equals(nsCOMTypeInfo::GetIID())) { + *aInstancePtr = (void*) ((nsIEventSinkGetter*)this); + NS_ADDREF_THIS(); + return NS_OK; + } return NS_ERROR_NO_INTERFACE; } diff --git a/mozilla/xpfe/components/xfer/src/nsStreamXferOp.h b/mozilla/xpfe/components/xfer/src/nsStreamXferOp.h index 242166435e9..462d92b6978 100644 --- a/mozilla/xpfe/components/xfer/src/nsStreamXferOp.h +++ b/mozilla/xpfe/components/xfer/src/nsStreamXferOp.h @@ -22,13 +22,16 @@ #ifndef __nsStreamXferOp_h #define __nsStreamXferOp_h -#include "nsString.h" #include "nsIStreamTransferOperation.h" -#include "nsIStreamListener.h" +#include "nsIEventSinkGetter.h" #include "nsIProgressEventSink.h" +#include "nsIStreamListener.h" + +#include "nsCOMPtr.h" class nsIDOMWindow; -class nsOutputFileStream; +class nsIChannel; +class nsIFileSpec; // Implementation of the stream transfer operation interface. // @@ -37,21 +40,27 @@ class nsOutputFileStream; // passed to a newly-created downloadProgress.xul dialog. That dialog "owns" // the object (and the creator releases it immediately). The object's dtor // should get called when the dialog closes. +// class nsStreamXferOp : public nsIStreamTransferOperation, + public nsIEventSinkGetter, public nsIProgressEventSink, public nsIStreamListener { public: // ctor/dtor - nsStreamXferOp( const nsString &source, const nsString &target ); + nsStreamXferOp( nsIChannel *source, nsIFileSpec *target ); virtual ~nsStreamXferOp(); // Implementation. NS_IMETHOD OpenDialog( nsIDOMWindow *parent ); + NS_IMETHOD OnError( int operation, nsresult rv ); // Declare inherited interfaces. NS_DECL_ISUPPORTS NS_DECL_NSISTREAMTRANSFEROPERATION + // nsIEventSinkGetter methods: + NS_DECL_NSIEVENTSINKGETTER + // nsIProgressEventSink methods: NS_DECL_NSIPROGRESSEVENTSINK @@ -62,15 +71,14 @@ public: NS_DECL_NSISTREAMLISTENER private: - nsCString mSource; - nsCString mTarget; - nsIObserver *mObserver; // Not owned; owner should call SetObserver(0) prior - // to this object getting destroyed. - PRUint32 mBufLen; - char *mBuffer; // Owned; deleted in dtor. - PRBool mStopped; - nsOutputFileStream *mOutput; // Owned; deleted in dtor. + nsCOMPtr mInputChannel; + nsCOMPtr mOutputChannel; + nsCOMPtr mOutputStream; + nsCOMPtr mOutputSpec; + nsIObserver *mObserver; // Not owned; owner should call SetObserver(0) prior + // to this object getting destroyed. + int mContentLength; + unsigned long mBytesProcessed; }; // nsStreamXferOp - #endif