Bugs 129614/129604/137676; ensure all download errors are shown to user (and fix download manager quirk so it doesn't think all downloads are 0kb of 0kb); r=bzbarsky, sr=blake

This commit is contained in:
law%netscape.com 2002-05-17 00:31:53 +00:00
Родитель 0aa5387f3b
Коммит 8b9240bcab
12 изменённых файлов: 200 добавлений и 54 удалений

Просмотреть файл

@ -63,6 +63,7 @@ function nsHelperAppDialog() {
this.strings = new Array;
this.elements = new Array;
this.updateSelf = true;
this.mTitle = "";
}
nsHelperAppDialog.prototype = {
@ -105,6 +106,9 @@ nsHelperAppDialog.prototype = {
null );
// Hook this object to the dialog.
this.mDialog.dialog = this;
// Watch for error notifications.
this.progressListener.helperAppDlg = this;
this.mLauncher.setWebProgressListener( this.progressListener );
},
// promptForSaveToFile: Display file picker dialog and return selected file.
@ -191,6 +195,49 @@ nsHelperAppDialog.prototype = {
// ---------- implementation methods ----------
// Web progress listener so we can detect errors while mLauncher is
// streaming the data to a temporary file.
progressListener: {
// Implementation properties.
helperAppDlg: null,
// nsIWebProgressListener methods.
// Look for error notifications and display alert to user.
onStatusChange: function( aWebProgress, aRequest, aStatus, aMessage ) {
if ( aStatus != Components.results.NS_OK ) {
// Get prompt service.
var prompter = Components.classes[ "@mozilla.org/embedcomp/prompt-service;1" ]
.getService( Components.interfaces.nsIPromptService );
// Display error alert (using text supplied by back-end).
prompter.alert( this.dialog, this.helperAppDlg.mTitle, aMessage );
// Close the dialog.
this.helperAppDlg.onCancel();
if ( this.helperAppDlg.mDialog ) {
this.helperAppDlg.mDialog.close();
}
}
},
// Ignore onProgressChange, onStateChange, onLocationChange, and onSecurityChange notifications.
onProgressChange: function( aWebProgress,
aRequest,
aCurSelfProgress,
aMaxSelfProgress,
aCurTotalProgress,
aMaxTotalProgress ) {
},
onStateChange: function( aWebProgress, aRequest, aStateFlags, aStatus ) {
},
onLocationChange: function( aWebProgress, aRequest, aLocation ) {
},
onSecurityChange: function( aWebProgress, aRequest, state ) {
},
},
// initDialog: Fill various dialog fields with initial content.
initDialog : function() {
// Check if file is executable (in which case, we will go straight to
@ -200,6 +247,9 @@ nsHelperAppDialog.prototype = {
var tmpFile = this.mLauncher.getDownloadInfo( ignore1, ignore2 );
if ( tmpFile.isExecutable() ) {
this.mLauncher.saveToDisk( null, false );
// Make sure onunload handler doesn't cancel.
this.mDialog.dialog = null;
// Close the dialog.
this.mDialog.close();
return;
}
@ -232,8 +282,8 @@ nsHelperAppDialog.prototype = {
fname = suggestedFileName;
var title = this.replaceInsert( win.getAttribute( "title" ), 1, fname);
win.setAttribute( "title", title );
this.mTitle = this.replaceInsert( win.getAttribute( "title" ), 1, fname);
win.setAttribute( "title", this.mTitle );
// Put content type and location into intro.
this.initIntro(url);
@ -414,6 +464,10 @@ nsHelperAppDialog.prototype = {
this.processAlwaysAskState();
// Remove our web progress listener (a progress dialog will be
// taking over).
this.mLauncher.setWebProgressListener( null );
if ( this.dialogElement( "openUsing" ).selected )
{
// If no app "chosen" then convert input string to file.
@ -435,6 +489,9 @@ nsHelperAppDialog.prototype = {
// onCancel:
onCancel: function() {
// Remove our web progress listener.
this.mLauncher.setWebProgressListener( null );
// Cancel app launcher.
try {
this.mLauncher.Cancel();

Просмотреть файл

@ -39,6 +39,7 @@
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="&caption.label;"
onload="dialog.initDialog()"
onunload="if (dialog) dialog.onCancel()"
style="width: 40em;"
class="dialog">

Просмотреть файл

@ -581,8 +581,11 @@ nsProgressDialog.prototype = {
// Activate reveal/launch buttons.
this.enable( "reveal" );
if ( this.target && !this.target.isExecutable() ) {
this.enable( "launch" );
try {
if ( this.target && !this.target.isExecutable() ) {
this.enable( "launch" );
}
} catch(e) {
}
// Disable the Pause/Resume buttons.

Просмотреть файл

@ -41,4 +41,4 @@ launchError=%S could not be opened, because an unknown error occurred.\n\nSorry
diskFull=There is not enough room on the disk to save %S.\n\nRemove unnecessary files from the disk and try again, or try saving in a different location.
readOnly=%S could not be saved, because the disk, folder, or file is write-protected.\n\nWrite-enable the disk and try again, or try saving in a different location.
accessError=%S could not be saved, because you cannot change the contents of that folder.\n\nChange the folder properties and try again, or try saving in a different location.
title=Downloading %S

Просмотреть файл

@ -1707,7 +1707,8 @@ nsTreeBodyFrame::PrefillPropertyArray(PRInt32 aRowIndex, nsTreeColumn* aCol)
mScratchArray->AppendElement(nsXULAtoms::progressmeter);
PRInt32 state = nsITreeView::progressNone;
mView->GetProgressMode(aRowIndex, aCol->GetID().get(), &state);
if (aRowIndex != -1)
mView->GetProgressMode(aRowIndex, aCol->GetID().get(), &state);
if (state == nsITreeView::progressNormal)
mScratchArray->AppendElement(nsXULAtoms::progressNormal);
else if (state == nsITreeView::progressUndetermined)

Просмотреть файл

@ -53,6 +53,7 @@ REQUIRES = xpcom \
plugin \
pref \
intl \
windowwatcher \
$(NULL)
OSHELPER = nsOSHelperAppService.cpp

Просмотреть файл

@ -33,6 +33,7 @@ REQUIRES = xpcom \
plugin \
unicharutil \
intl \
windowwatcher \
$(NULL)
include <$(DEPTH)\config\config.mak>

Просмотреть файл

@ -78,6 +78,8 @@
#include "nsEscape.h"
#include "nsIStringBundle.h"
#include "nsIPromptService.h"
#include "nsIDOMWindow.h"
const char *FORCE_ALWAYS_ASK_PREF = "browser.helperApps.alwaysAsk.force";
@ -758,23 +760,17 @@ NS_IMETHODIMP nsExternalAppHandler::Observe(nsISupports *aSubject, const char *a
NS_IMETHODIMP nsExternalAppHandler::SetWebProgressListener(nsIWebProgressListener * aWebProgressListener)
{
// this call back means we've succesfully brought up the
// progress window so set the appropriate flag...
mProgressWindowCreated = PR_TRUE;
// while we were bringing up the progress dialog, we actually finished processing the
// url. If that's the case then mStopRequestIssued will be true. Tell the dialog to go away in that
// case and we need to execute the operation since we are actually done.
if (mStopRequestIssued && aWebProgressListener)
// The progress window only appears after the helper app dialog has told us
// what to do. We need to be careful not to confuse helper app dialog
// calls to this method with ones from the progress dialog!
if (mReceivedDispostionInfo && aWebProgressListener)
{
// simulate a notification saying the document is done. This will turn around and cause our
// progress dialog to go away....
aWebProgressListener->OnStateChange(nsnull, nsnull, nsIWebProgressListener::STATE_STOP, NS_OK);
return ExecuteDesiredAction();
// this call back means we've succesfully brought up the
// progress window so set the appropriate flag...
mProgressWindowCreated = PR_TRUE;
}
// o.t. go ahead and register the progress listener....
// Go ahead and register the progress listener....
if (mLoadCookie)
{
@ -785,6 +781,15 @@ NS_IMETHODIMP nsExternalAppHandler::SetWebProgressListener(nsIWebProgressListene
mWebProgressListener = aWebProgressListener;
}
}
// while we were bringing up the progress dialog, we actually finished processing the
// url. If that's the case then mStopRequestIssued will be true. We need to execute the
// operation since we are actually done now.
if (mStopRequestIssued && aWebProgressListener)
{
return ExecuteDesiredAction();
}
return NS_OK;
}
@ -1192,9 +1197,7 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest *request, nsISuppo
// Convert error info into proper message text and send OnStatusChange notification
// to the web progress listener.
typedef enum { kReadError, kWriteError, kLaunchError } ErrorType;
static void SendStatusChange(
ErrorType type, nsresult rv, nsIRequest *aRequest, nsIWebProgressListener *aListener, const nsAFlatString &path)
void nsExternalAppHandler::SendStatusChange(ErrorType type, nsresult rv, nsIRequest *aRequest, const nsAFlatString &path)
{
nsAutoString msgId;
switch(rv)
@ -1231,6 +1234,9 @@ static void SendStatusChange(
}
break;
}
#ifdef DEBUG_law
printf( "\nError: %s, listener=0x%08X, rv=0x%08X\n\n", NS_LossyConvertUCS2toASCII(msgId).get(), (int)(void*)mWebProgressListener.get(), (int)rv );
#endif
// Get properties file bundle and extract status string.
nsCOMPtr<nsIStringBundleService> s = do_GetService(NS_STRINGBUNDLE_CONTRACTID);
if (s)
@ -1242,7 +1248,27 @@ static void SendStatusChange(
const PRUnichar *strings[] = { path.get() };
if(NS_SUCCEEDED(bundle->FormatStringFromName(msgId.get(), strings, 1, getter_Copies(msgText))))
{
aListener->OnStatusChange(nsnull, (type == kReadError) ? aRequest : nsnull, rv, msgText);
if (mWebProgressListener)
{
// We have a listener, let it handle the error.
mWebProgressListener->OnStatusChange(nsnull, (type == kReadError) ? aRequest : nsnull, rv, msgText);
}
else
{
// We don't have a listener. Simply show the alert ourselves.
nsCOMPtr<nsIPromptService> prompter(do_GetService("@mozilla.org/embedcomp/prompt-service;1"));
nsXPIDLString title;
bundle->FormatStringFromName(NS_LITERAL_STRING("title").get(),
strings,
1,
getter_Copies(title));
if (prompter)
{
// Extract parent window from context.
nsCOMPtr<nsIDOMWindow> parent(do_GetInterface(mWindowContext));
prompter->Alert(parent, title, msgText);
}
}
}
}
}
@ -1318,13 +1344,13 @@ NS_IMETHODIMP nsExternalAppHandler::OnDataAvailable(nsIRequest *request, nsISupp
else
{
// An error occurred, notify listener.
if (mWebProgressListener)
{
nsAutoString tempFilePath;
if (mTempFile)
mTempFile->GetPath(tempFilePath);
SendStatusChange(readError ? kReadError : kWriteError, rv, request, mWebProgressListener, tempFilePath);
}
nsAutoString tempFilePath;
if (mTempFile)
mTempFile->GetPath(tempFilePath);
SendStatusChange(readError ? kReadError : kWriteError, rv, request, tempFilePath);
// Cancel the download.
Cancel();
}
}
return rv;
@ -1335,15 +1361,21 @@ NS_IMETHODIMP nsExternalAppHandler::OnStopRequest(nsIRequest *request, nsISuppor
{
mStopRequestIssued = PR_TRUE;
// Cancel if the request did not complete successfully.
if (!mCanceled && NS_FAILED(aStatus))
{
// Send error notification.
nsAutoString tempFilePath;
if (mTempFile)
mTempFile->GetPath(tempFilePath);
SendStatusChange( kReadError, aStatus, request, tempFilePath );
Cancel();
}
// first, check to see if we've been canceled....
if (mCanceled) { // then go cancel our underlying channel too
nsresult rv = request->Cancel(NS_BINDING_ABORTED);
// Notify dialog that download is complete.
if(mWebProgressListener)
{
// XXX Do we need to check for errors here (server goes down, network cable cut, etc.)?
mWebProgressListener->OnStateChange(nsnull, request, nsIWebProgressListener::STATE_STOP, NS_OK);
}
return rv;
}
@ -1359,13 +1391,6 @@ NS_IMETHODIMP nsExternalAppHandler::OnStopRequest(nsIRequest *request, nsISuppor
mOutStream = nsnull;
}
// Notify dialog that download is complete.
if(mWebProgressListener)
{
// XXX Do we need to check for errors here (server goes down, network cable cut, etc.)?
mWebProgressListener->OnStateChange(nsnull, request, nsIWebProgressListener::STATE_STOP, NS_OK);
}
return ExecuteDesiredAction();
}
@ -1377,6 +1402,8 @@ nsresult nsExternalAppHandler::ExecuteDesiredAction()
nsMIMEInfoHandleAction action = nsIMIMEInfo::saveToDisk;
mMimeInfo->GetPreferredAction(&action);
if (action == nsIMIMEInfo::saveToDisk)
// XXX Put progress dialog in barber-pole mode
// and change text to say "Copying from:".
rv = MoveFile(mFinalFileDestination);
else
{
@ -1392,6 +1419,18 @@ nsresult nsExternalAppHandler::ExecuteDesiredAction()
rv = OpenWithApplication(nsnull);
}
}
// Notify dialog that download is complete.
// By waiting till this point, it ensures that the progress dialog doesn't indicate
// success until we're really done.
if(mWebProgressListener)
{
if (!mCanceled)
{
mWebProgressListener->OnProgressChange(nsnull, nsnull, mContentLength, mContentLength, mContentLength, mContentLength);
}
mWebProgressListener->OnStateChange(nsnull, nsnull, nsIWebProgressListener::STATE_STOP, NS_OK);
}
}
return rv;
@ -1535,12 +1574,13 @@ nsresult nsExternalAppHandler::MoveFile(nsIFile * aNewFileLocation)
{
rv = mTempFile->MoveToNative(directoryLocation, fileName);
}
if (NS_FAILED(rv) && mWebProgressListener)
if (NS_FAILED(rv))
{
// Send error notification.
nsAutoString path;
fileToUse->GetPath(path);
SendStatusChange(kWriteError, rv, nsnull, mWebProgressListener, path);
SendStatusChange(kWriteError, rv, nsnull, path);
Cancel(); // Cancel (and clean up temp file).
}
}
@ -1563,6 +1603,9 @@ NS_IMETHODIMP nsExternalAppHandler::SaveToDisk(nsIFile * aNewFileLocation, PRBoo
mMimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk);
// The helper app dialog has told us what to do.
mReceivedDispostionInfo = PR_TRUE;
if (!aNewFileLocation)
{
nsAutoString leafName;
@ -1597,7 +1640,6 @@ NS_IMETHODIMP nsExternalAppHandler::SaveToDisk(nsIFile * aNewFileLocation, PRBoo
ProcessAnyRefreshTags();
}
mReceivedDispostionInfo = PR_TRUE;
return rv;
}
@ -1618,19 +1660,22 @@ nsresult nsExternalAppHandler::OpenWithApplication(nsIFile * aApplication)
if (helperAppService)
{
rv = helperAppService->LaunchAppWithTempFile(mMimeInfo, mFinalFileDestination);
if (NS_FAILED(rv) && mWebProgressListener)
if (NS_FAILED(rv))
{
// Send error notification.
nsAutoString path;
mFinalFileDestination->GetPath(path);
SendStatusChange(kLaunchError, rv, nsnull, mWebProgressListener, path);
SendStatusChange(kLaunchError, rv, nsnull, path);
Cancel(); // Cancel, and clean up temp file.
}
else
{
#ifndef XP_MAC
// Mac users have been very verbal about temp files being deleted on app exit - they
// don't like it - but we'll continue to do this on other platforms for now
helperAppService->DeleteTemporaryFileOnExit(mFinalFileDestination);
#endif
}
}
}

Просмотреть файл

@ -227,6 +227,9 @@ protected:
// the base class implementation ensures that mSuggestedFileName has
// mTempFileExtension as extension;
virtual void EnsureSuggestedFileName();
// utility function to send proper error notification to web progress listener
typedef enum { kReadError, kWriteError, kLaunchError } ErrorType;
void SendStatusChange(ErrorType type, nsresult aStatus, nsIRequest *aRequest, const nsAFlatString &path);
nsCOMPtr<nsISupports> mLoadCookie; // load cookie used by the uri loader when we fetch the url
nsCOMPtr<nsIWebProgressListener> mWebProgressListener;

Просмотреть файл

@ -130,8 +130,13 @@ var downloadViewController = {
var isDownloading = gDownloadManager.getDownload(selectedItem.id);
switch (aCommand) {
case "cmd_openfile":
if (!isDownloading && getFileForItem(selectedItem).isExecutable())
try {
if (!isDownloading && getFileForItem(selectedItem).isExecutable())
return false;
} catch(e) {
// Exception means file doesn't exist; launch is not allowed.
return false;
}
case "cmd_showinshell":
// some apps like kazaa/morpheus let you "preview" in-progress downloads because

Просмотреть файл

@ -4,6 +4,7 @@ notStarted=Not Started
failed=Failed
finished=Finished
canceled=Canceled
alertTitle=Download Error
showInShellLabelWin=Show in Explorer
showInShellAccesskeyWin=E

Просмотреть файл

@ -56,6 +56,7 @@
#include "nsIStringBundle.h"
#include "nsCRT.h"
#include "nsIWindowMediator.h"
#include "nsIPromptService.h"
/* Outstanding issues/todo:
* 1. Implement pause/resume.
@ -1032,9 +1033,8 @@ nsDownload::OnStatusChange(nsIWebProgress *aWebProgress,
mDownloadState = FAILED;
nsCAutoString path;
nsresult rv = mTarget->GetNativePath(path);
if (NS_FAILED(rv)) return rv;
mDownloadManager->DownloadEnded(path.get(), aMessage);
if (NS_SUCCEEDED(rv))
mDownloadManager->DownloadEnded(path.get(), aMessage);
}
if (mListener)
@ -1049,6 +1049,31 @@ nsDownload::OnStatusChange(nsIWebProgress *aWebProgress,
if (mDialogListener)
mDialogListener->OnStatusChange(aWebProgress, aRequest, aStatus, aMessage);
else {
// Need to display error alert ourselves, if an error occurred.
if (NS_FAILED(aStatus)) {
// Get title for alert.
nsXPIDLString title;
nsresult rv;
nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(kStringBundleServiceCID, &rv);
nsCOMPtr<nsIStringBundle> bundle;
if (bundleService)
rv = bundleService->CreateBundle(DOWNLOAD_MANAGER_BUNDLE, getter_AddRefs(bundle));
if (bundle)
bundle->GetStringFromName(NS_LITERAL_STRING("alertTitle").get(), getter_Copies(title));
// Get Download Manager window, to be parent of alert.
nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv);
nsCOMPtr<nsIDOMWindowInternal> dmWindow;
if (wm)
wm->GetMostRecentWindow(NS_LITERAL_STRING("Download:Manager").get(), getter_AddRefs(dmWindow));
// Show alert.
nsCOMPtr<nsIPromptService> prompter(do_GetService("@mozilla.org/embedcomp/prompt-service;1"));
if (prompter)
prompter->Alert(dmWindow, title, aMessage);
}
}
return NS_OK;
}
@ -1077,6 +1102,9 @@ nsDownload::OnStateChange(nsIWebProgress* aWebProgress,
if (aStateFlags & STATE_STOP) {
if (mDownloadState == DOWNLOADING || mDownloadState == NOTSTARTED) {
mDownloadState = FINISHED;
// Files less than 1Kb shouldn't show up as 0Kb.
if (mMaxBytes==0)
mMaxBytes = 1;
mCurrBytes = mMaxBytes;
mPercentComplete = 100;