зеркало из https://github.com/mozilla/pjs.git
Bug 27609; change exthandler to use new progress dialog and properly report i/o errors; r=bzbarsky, sr=mscott
This commit is contained in:
Родитель
c292c09c27
Коммит
58689daf71
|
@ -49,8 +49,10 @@ REQUIRES = xpcom \
|
|||
rdf \
|
||||
webshell \
|
||||
helperAppDlg \
|
||||
progressDlg \
|
||||
plugin \
|
||||
pref \
|
||||
intl \
|
||||
$(NULL)
|
||||
|
||||
OSHELPER = nsOSHelperAppService.cpp
|
||||
|
|
|
@ -29,8 +29,10 @@ REQUIRES = xpcom \
|
|||
rdf \
|
||||
webshell \
|
||||
helperAppDlg \
|
||||
progressDlg \
|
||||
plugin \
|
||||
unicharutil \
|
||||
intl \
|
||||
$(NULL)
|
||||
|
||||
include <$(DEPTH)\config\config.mak>
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
#include "nsIRefreshURI.h"
|
||||
#include "nsIDocumentLoader.h"
|
||||
#include "nsIHelperAppLauncherDialog.h"
|
||||
#include "nsIProgressDialog.h"
|
||||
#include "nsITransport.h"
|
||||
#include "nsIFileTransportService.h"
|
||||
#include "nsCExternalHandlerService.h" // contains contractids for the helper app service
|
||||
|
@ -75,6 +76,8 @@
|
|||
#include "nsIPluginHost.h"
|
||||
#include "nsEscape.h"
|
||||
|
||||
#include "nsIStringBundle.h"
|
||||
|
||||
const char *FORCE_ALWAYS_ASK_PREF = "browser.helperApps.alwaysAsk.force";
|
||||
|
||||
static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
|
||||
|
@ -673,6 +676,7 @@ NS_INTERFACE_MAP_BEGIN(nsExternalAppHandler)
|
|||
NS_INTERFACE_MAP_ENTRY(nsIHelperAppLauncher)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIURIContentListener)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
||||
NS_INTERFACE_MAP_END_THREADSAFE
|
||||
|
||||
nsExternalAppHandler::nsExternalAppHandler()
|
||||
|
@ -683,6 +687,8 @@ nsExternalAppHandler::nsExternalAppHandler()
|
|||
mStopRequestIssued = PR_FALSE;
|
||||
mDataBuffer = (char *) nsMemory::Alloc((sizeof(char) * DATA_BUFFER_SIZE));
|
||||
mProgressWindowCreated = PR_FALSE;
|
||||
mContentLength = -1;
|
||||
mProgress = 0;
|
||||
}
|
||||
|
||||
nsExternalAppHandler::~nsExternalAppHandler()
|
||||
|
@ -697,6 +703,16 @@ NS_IMETHODIMP nsExternalAppHandler::GetInterface(const nsIID & aIID, void * *aIn
|
|||
return QueryInterface(aIID, aInstancePtr);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsExternalAppHandler::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData )
|
||||
{
|
||||
if (PL_strcmp(aTopic, "oncancel") == 0)
|
||||
{
|
||||
// User pressed cancel button on dialog.
|
||||
return this->Cancel();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP nsExternalAppHandler::SetWebProgressListener(nsIWebProgressListener * aWebProgressListener)
|
||||
{
|
||||
|
@ -725,7 +741,6 @@ NS_IMETHODIMP nsExternalAppHandler::SetWebProgressListener(nsIWebProgressListene
|
|||
if (webProgress)
|
||||
{
|
||||
mWebProgressListener = aWebProgressListener;
|
||||
webProgress->AddProgressListener(mWebProgressListener);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
|
@ -1011,6 +1026,11 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest *request, nsISuppo
|
|||
|
||||
nsresult rv = SetUpTempFile(aChannel);
|
||||
|
||||
// Get content length.
|
||||
if ( aChannel )
|
||||
{
|
||||
aChannel->GetContentLength( &mContentLength );
|
||||
}
|
||||
|
||||
// retarget all load notifcations to our docloader instead of the original window's docloader...
|
||||
RetargetLoadNotifications(request);
|
||||
|
@ -1023,6 +1043,8 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest *request, nsISuppo
|
|||
httpChannel->SetApplyConversion( PR_FALSE );
|
||||
}
|
||||
|
||||
mTimeDownloadStarted = PR_Now();
|
||||
|
||||
// now that the temp file is set up, find out if we need to invoke a dialog asking the user what
|
||||
// they want us to do with this content...
|
||||
|
||||
|
@ -1059,14 +1081,71 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest *request, nsISuppo
|
|||
}
|
||||
}
|
||||
|
||||
mTimeDownloadStarted = PR_Now();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// 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 PRUnichar *path)
|
||||
{
|
||||
nsAutoString msgId;
|
||||
switch(rv)
|
||||
{
|
||||
case NS_ERROR_FILE_DISK_FULL:
|
||||
case NS_ERROR_FILE_NO_DEVICE_SPACE:
|
||||
// Out of space on target volume.
|
||||
msgId = NS_LITERAL_STRING("diskFull");
|
||||
break;
|
||||
|
||||
case NS_ERROR_FILE_READ_ONLY:
|
||||
// Attempt to write to read/only file.
|
||||
msgId = NS_LITERAL_STRING("readOnly");
|
||||
break;
|
||||
|
||||
case NS_ERROR_FILE_ACCESS_DENIED:
|
||||
// Attempt to write without sufficient permissions.
|
||||
msgId = NS_LITERAL_STRING("accessError");
|
||||
break;
|
||||
|
||||
default:
|
||||
// Generic read/write/launch error message.
|
||||
switch(type)
|
||||
{
|
||||
case kReadError:
|
||||
msgId = NS_LITERAL_STRING("readError");
|
||||
break;
|
||||
case kWriteError:
|
||||
msgId = NS_LITERAL_STRING("writeError");
|
||||
break;
|
||||
case kLaunchError:
|
||||
msgId = NS_LITERAL_STRING("launchError");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Get properties file bundle and extract status string.
|
||||
nsCOMPtr<nsIStringBundleService> s = do_GetService(NS_STRINGBUNDLE_CONTRACTID);
|
||||
if (s)
|
||||
{
|
||||
nsCOMPtr<nsIStringBundle> bundle;
|
||||
if (NS_SUCCEEDED(s->CreateBundle("chrome://global/locale/nsWebBrowserPersist.properties", getter_AddRefs(bundle))))
|
||||
{
|
||||
nsXPIDLString msgText;
|
||||
const PRUnichar *strings[] = { path };
|
||||
if(NS_SUCCEEDED(bundle->FormatStringFromName(msgId.get(), strings, 1, getter_Copies(msgText))))
|
||||
{
|
||||
aListener->OnStatusChange(nsnull, (type == kReadError) ? aRequest : nsnull, rv, msgText);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsExternalAppHandler::OnDataAvailable(nsIRequest *request, nsISupports * aCtxt,
|
||||
nsIInputStream * inStr, PRUint32 sourceOffset, PRUint32 count)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
// first, check to see if we've been canceled....
|
||||
if (mCanceled) // then go cancel our underlying channel too
|
||||
return request->Cancel(NS_BINDING_ABORTED);
|
||||
|
@ -1076,17 +1155,73 @@ NS_IMETHODIMP nsExternalAppHandler::OnDataAvailable(nsIRequest *request, nsISupp
|
|||
{
|
||||
PRUint32 numBytesRead = 0;
|
||||
PRUint32 numBytesWritten = 0;
|
||||
while (count > 0) // while we still have bytes to copy...
|
||||
mProgress += count;
|
||||
PRBool readError;
|
||||
while (NS_SUCCEEDED(rv) && count > 0) // while we still have bytes to copy...
|
||||
{
|
||||
inStr->Read(mDataBuffer, PR_MIN(count, DATA_BUFFER_SIZE - 1), &numBytesRead);
|
||||
if (count >= numBytesRead)
|
||||
count -= numBytesRead; // subtract off the number of bytes we just read
|
||||
else
|
||||
count = 0;
|
||||
mOutStream->Write(mDataBuffer, numBytesRead, &numBytesWritten);
|
||||
readError = PR_TRUE;
|
||||
rv = inStr->Read(mDataBuffer, PR_MIN(count, DATA_BUFFER_SIZE - 1), &numBytesRead);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
{
|
||||
if (count >= numBytesRead)
|
||||
count -= numBytesRead; // subtract off the number of bytes we just read
|
||||
else
|
||||
count = 0;
|
||||
readError = PR_FALSE;
|
||||
// Write out the data until something goes wrong, or, it is
|
||||
// all written. We loop because for some errors (e.g., disk
|
||||
// full), we get NS_OK with some bytes written, then an error.
|
||||
// So, we want to write again in that case to get the actual
|
||||
// error code.
|
||||
const char *bufPtr = mDataBuffer; // Where to write from.
|
||||
while (NS_SUCCEEDED(rv) && numBytesRead)
|
||||
{
|
||||
numBytesWritten = 0;
|
||||
rv = mOutStream->Write(bufPtr, numBytesRead, &numBytesWritten);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
{
|
||||
numBytesRead -= numBytesWritten;
|
||||
bufPtr += numBytesWritten;
|
||||
// Force an error if (for some reason) we get NS_OK but
|
||||
// no bytes written.
|
||||
if (!numBytesWritten)
|
||||
{
|
||||
rv = NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (NS_SUCCEEDED(rv))
|
||||
{
|
||||
// Set content length if we haven't already got it.
|
||||
if (mContentLength == -1)
|
||||
{
|
||||
nsCOMPtr<nsIChannel> aChannel(do_QueryInterface(request));
|
||||
if (aChannel)
|
||||
{
|
||||
aChannel->GetContentLength(&mContentLength);
|
||||
}
|
||||
}
|
||||
// Send progress notification.
|
||||
if (mWebProgressListener)
|
||||
{
|
||||
mWebProgressListener->OnProgressChange(nsnull, request, mProgress, mContentLength, mProgress, mContentLength);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// An error occurred, notify listener.
|
||||
if (mWebProgressListener)
|
||||
{
|
||||
nsXPIDLString tempFilePath;
|
||||
if (mTempFile)
|
||||
mTempFile->GetUnicodePath(getter_Copies(tempFilePath));
|
||||
SendStatusChange(readError ? kReadError : kWriteError, rv, request, mWebProgressListener, tempFilePath.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsExternalAppHandler::OnStopRequest(nsIRequest *request, nsISupports *aCtxt,
|
||||
|
@ -1110,6 +1245,13 @@ 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();
|
||||
}
|
||||
|
||||
|
@ -1181,13 +1323,49 @@ nsresult nsExternalAppHandler::ShowProgressDialog()
|
|||
// done processing the load. in this case, throw up a progress dialog so the user can see what's going on...
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if ( !mDialog ) {
|
||||
// Get helper app launcher dialog.
|
||||
mDialog = do_CreateInstance( NS_IHELPERAPPLAUNCHERDLG_CONTRACTID, &rv );
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
nsCOMPtr<nsIProgressDialog> progressDlg = do_CreateInstance( "@mozilla.org/progressdialog;1", &rv );
|
||||
if (progressDlg)
|
||||
{
|
||||
// Wire up this progress dialog.
|
||||
progressDlg->SetSource( mSourceUrl );
|
||||
progressDlg->SetStartTime( mTimeDownloadStarted );
|
||||
progressDlg->SetObserver(this);
|
||||
nsCOMPtr<nsILocalFile> local = do_QueryInterface(mFinalFileDestination);
|
||||
progressDlg->SetTarget(local);
|
||||
|
||||
rv = mDialog->ShowProgressDialog(this, mWindowContext);
|
||||
nsMIMEInfoHandleAction action = nsIMIMEInfo::saveToDisk;
|
||||
mMimeInfo->GetPreferredAction(&action);
|
||||
if (action != nsIMIMEInfo::saveToDisk)
|
||||
{
|
||||
// Opening with an application; use either description or application file name.
|
||||
nsXPIDLString openWith;
|
||||
mMimeInfo->GetApplicationDescription(getter_Copies(openWith));
|
||||
if (openWith.IsEmpty())
|
||||
{
|
||||
nsCOMPtr<nsIFile> appl;
|
||||
mMimeInfo->GetPreferredApplicationHandler(getter_AddRefs(appl));
|
||||
if (appl)
|
||||
{
|
||||
nsCOMPtr<nsILocalFile> file = do_QueryInterface(appl);
|
||||
if (file)
|
||||
{
|
||||
file->GetUnicodeLeafName(getter_Copies(openWith));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Tell progress dialog what we're opening with.
|
||||
progressDlg->SetOpeningWith(openWith);
|
||||
}
|
||||
|
||||
// Open the dialog.
|
||||
rv = progressDlg->Open(nsnull, nsnull);
|
||||
|
||||
if(NS_SUCCEEDED(rv))
|
||||
{
|
||||
// Send notifications to the dialog.
|
||||
this->SetWebProgressListener(progressDlg);
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
@ -1242,6 +1420,13 @@ nsresult nsExternalAppHandler::MoveFile(nsIFile * aNewFileLocation)
|
|||
{
|
||||
rv = mTempFile->MoveTo(directoryLocation, fileName);
|
||||
}
|
||||
if (NS_FAILED(rv) && mWebProgressListener)
|
||||
{
|
||||
// Send error notification.
|
||||
nsXPIDLString path;
|
||||
fileToUse->GetUnicodePath(getter_Copies(path));
|
||||
SendStatusChange(kWriteError, rv, nsnull, mWebProgressListener, path.get());
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
|
@ -1282,7 +1467,7 @@ NS_IMETHODIMP nsExternalAppHandler::SaveToDisk(nsIFile * aNewFileLocation, PRBoo
|
|||
rv = PromptForSaveToFile(getter_AddRefs(fileToUse), mSuggestedFileName.get(), fileExt.get());
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
if (NS_FAILED(rv) || !fileToUse)
|
||||
return Cancel();
|
||||
|
||||
mFinalFileDestination = do_QueryInterface(fileToUse);
|
||||
|
@ -1318,6 +1503,13 @@ nsresult nsExternalAppHandler::OpenWithApplication(nsIFile * aApplication)
|
|||
if (helperAppService)
|
||||
{
|
||||
rv = helperAppService->LaunchAppWithTempFile(mMimeInfo, mFinalFileDestination);
|
||||
if (NS_FAILED(rv) && mWebProgressListener)
|
||||
{
|
||||
// Send error notification.
|
||||
nsXPIDLString path;
|
||||
mFinalFileDestination->GetUnicodePath(getter_Copies(path));
|
||||
SendStatusChange(kLaunchError, rv, nsnull, mWebProgressListener,path.get());
|
||||
}
|
||||
|
||||
#ifndef XP_MAC
|
||||
// Mac users have been very verbal about temp files being deleted on app exit - they
|
||||
|
@ -1343,10 +1535,6 @@ NS_IMETHODIMP nsExternalAppHandler::LaunchWithApplication(nsIFile * aApplication
|
|||
|
||||
mMimeInfo->SetPreferredAction(nsIMIMEInfo::useHelperApp);
|
||||
|
||||
// launch the progress window now that the user has picked the desired action.
|
||||
if (!mProgressWindowCreated)
|
||||
ShowProgressDialog();
|
||||
|
||||
// user has chosen to launch using an application, fire any refresh tags now...
|
||||
ProcessAnyRefreshTags();
|
||||
|
||||
|
@ -1384,6 +1572,10 @@ NS_IMETHODIMP nsExternalAppHandler::LaunchWithApplication(nsIFile * aApplication
|
|||
|
||||
mFinalFileDestination = do_QueryInterface(fileToUse);
|
||||
|
||||
// launch the progress window now that the user has picked the desired action.
|
||||
if (!mProgressWindowCreated)
|
||||
ShowProgressDialog();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -150,7 +150,7 @@ struct nsDefaultMimeTypeEntry {
|
|||
#define DATA_BUFFER_SIZE (4096*2)
|
||||
|
||||
class nsExternalAppHandler : public nsIStreamListener, public nsIHelperAppLauncher, public nsIURIContentListener,
|
||||
public nsIInterfaceRequestor
|
||||
public nsIInterfaceRequestor, public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
@ -159,6 +159,7 @@ public:
|
|||
NS_DECL_NSIHELPERAPPLAUNCHER
|
||||
NS_DECL_NSIURICONTENTLISTENER
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
nsExternalAppHandler();
|
||||
virtual ~nsExternalAppHandler();
|
||||
|
@ -185,6 +186,8 @@ protected:
|
|||
PRBool mStopRequestIssued;
|
||||
PRBool mProgressWindowCreated;
|
||||
PRInt64 mTimeDownloadStarted;
|
||||
PRInt32 mContentLength;
|
||||
PRInt32 mProgress; // Number of bytes received (for sending progress notifications).
|
||||
|
||||
// when we are told to save the temp file to disk (in a more permament location) before we are done
|
||||
// writing the content to a temp file, then we need to remember the final destination until we are ready to
|
||||
|
|
Загрузка…
Ссылка в новой задаче