mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 03:45:46 +00:00
Fixes for #10737 (ftp download), overhauled to work better with Necko; r=valeski
This commit is contained in:
parent
1ea38abf06
commit
88626d4e62
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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<nsIFileSpec> mBaseFileSpec;
|
||||
|
||||
|
@ -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. |
|
||||
| |
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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<nsISupports> channel;
|
||||
|
||||
// Extract URI from channel.
|
||||
nsCOMPtr<nsIURI> 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<nsIScriptGlobalObject> sgo = do_QueryInterface( aWindow, &rv );
|
||||
if ( NS_SUCCEEDED( rv ) && sgo ) {
|
||||
nsCOMPtr<nsIScriptContext> 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<nsIScriptGlobalObject> sgo = do_QueryInterface( aWindow, &rv );
|
||||
if ( NS_SUCCEEDED( rv ) && sgo ) {
|
||||
nsCOMPtr<nsIScriptContext> 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<nsIDOMWindow> 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;
|
||||
|
@ -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 );
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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 <law@netscape.com>
|
||||
*/
|
||||
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 ) {
|
||||
|
@ -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<nsIFileSpec> 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<nsIStreamTransferOperation> 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<nsIURI> 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<nsIChannel> 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<nsIFileSpecWithUI> result;
|
||||
result = getter_AddRefs( NS_CreateFileSpecWithUI() );
|
||||
|
||||
if ( result ) {
|
||||
// Prompt for file name.
|
||||
nsCOMPtr<nsIFileSpec> 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<nsIURI> uri;
|
||||
nsresult rv = aChannel->GetURI( getter_AddRefs( uri ) );
|
||||
if ( NS_SUCCEEDED( rv ) && uri ) {
|
||||
// Try to get URL from URI.
|
||||
nsCOMPtr<nsIURL> 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,
|
||||
|
@ -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<nsIStreamTransferOperation>::GetIID()),
|
||||
(nsISupports*)(nsIStreamTransferOperation*)this );
|
||||
if ( argv ) {
|
||||
nsIDOMWindow *newWindow;
|
||||
rv = parent->OpenDialog( jsContext, argv, 4, &newWindow );
|
||||
if ( NS_SUCCEEDED( rv ) ) {
|
||||
newWindow->Release();
|
||||
} else {
|
||||
nsCOMPtr<nsIDOMWindow> 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<nsIChannel> 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<nsIOutputStream> 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<nsIProgressEventSink>::GetIID())) {
|
||||
*aInstancePtr = (void*) ((nsIProgressEventSink*)this);
|
||||
NS_ADDREF_THIS();
|
||||
return NS_OK;
|
||||
}
|
||||
if (aIID.Equals(nsCOMTypeInfo<nsISupports>::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<nsIProgressEventSink>::GetIID())) {
|
||||
*aInstancePtr = (void*) ((nsIProgressEventSink*)this);
|
||||
NS_ADDREF_THIS();
|
||||
return NS_OK;
|
||||
}
|
||||
if (aIID.Equals(nsCOMTypeInfo<nsIEventSinkGetter>::GetIID())) {
|
||||
*aInstancePtr = (void*) ((nsIEventSinkGetter*)this);
|
||||
NS_ADDREF_THIS();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
|
@ -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<nsIChannel> mInputChannel;
|
||||
nsCOMPtr<nsIChannel> mOutputChannel;
|
||||
nsCOMPtr<nsIOutputStream> mOutputStream;
|
||||
nsCOMPtr<nsIFileSpec> mOutputSpec;
|
||||
nsIObserver *mObserver; // Not owned; owner should call SetObserver(0) prior
|
||||
// to this object getting destroyed.
|
||||
int mContentLength;
|
||||
unsigned long mBytesProcessed;
|
||||
}; // nsStreamXferOp
|
||||
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user