|
|
|
@ -141,8 +141,8 @@ PRLogModuleInfo* nsExternalHelperAppService::mLog = nullptr;
|
|
|
|
|
// of PR_LOG_DEBUG (4), and we want less detailed output here
|
|
|
|
|
// Using 3 instead of PR_LOG_WARN because we don't output warnings
|
|
|
|
|
#undef LOG
|
|
|
|
|
#define LOG(args) PR_LOG(nsExternalHelperAppService::mLog, 3, args)
|
|
|
|
|
#define LOG_ENABLED() PR_LOG_TEST(nsExternalHelperAppService::mLog, 3)
|
|
|
|
|
#define LOG(args) PR_LOG(mLog, 3, args)
|
|
|
|
|
#define LOG_ENABLED() PR_LOG_TEST(mLog, 3)
|
|
|
|
|
|
|
|
|
|
static const char NEVER_ASK_FOR_SAVE_TO_DISK_PREF[] =
|
|
|
|
|
"browser.helperApps.neverAsk.saveToDisk";
|
|
|
|
@ -1111,13 +1111,14 @@ nsExternalAppHandler::nsExternalAppHandler(nsIMIMEInfo * aMIMEInfo,
|
|
|
|
|
, mForceSave(aForceSave)
|
|
|
|
|
, mCanceled(false)
|
|
|
|
|
, mShouldCloseWindow(false)
|
|
|
|
|
, mReceivedDispositionInfo(false)
|
|
|
|
|
, mStopRequestIssued(false)
|
|
|
|
|
, mProgressListenerInitialized(false)
|
|
|
|
|
, mReason(aReason)
|
|
|
|
|
, mContentLength(-1)
|
|
|
|
|
, mProgress(0)
|
|
|
|
|
, mSaver(nullptr)
|
|
|
|
|
, mDialogProgressListener(nullptr)
|
|
|
|
|
, mTransfer(nullptr)
|
|
|
|
|
, mKeepRequestAlive(false)
|
|
|
|
|
, mRequest(nullptr)
|
|
|
|
|
, mExtProtSvc(aExtProtSvc)
|
|
|
|
|
{
|
|
|
|
@ -1157,9 +1158,24 @@ nsExternalAppHandler::~nsExternalAppHandler()
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsExternalAppHandler::SetWebProgressListener(nsIWebProgressListener2 * aWebProgressListener)
|
|
|
|
|
{
|
|
|
|
|
// This is always called by nsHelperDlg.js. Go ahead and register the
|
|
|
|
|
// progress listener. At this point, we don't have mTransfer.
|
|
|
|
|
mDialogProgressListener = aWebProgressListener;
|
|
|
|
|
// this call back means we've successfully brought up the
|
|
|
|
|
// progress window so set the appropriate flag, even though
|
|
|
|
|
// aWebProgressListener might be null
|
|
|
|
|
|
|
|
|
|
if (mReceivedDispositionInfo)
|
|
|
|
|
mProgressListenerInitialized = true;
|
|
|
|
|
|
|
|
|
|
// Go ahead and register the progress listener....
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1197,6 +1213,13 @@ NS_IMETHODIMP nsExternalAppHandler::GetContentLength(int64_t *aContentLength)
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsExternalAppHandler::CloseProgressWindow()
|
|
|
|
|
{
|
|
|
|
|
// release extra state...
|
|
|
|
|
mWebProgressListener = nullptr;
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void nsExternalAppHandler::RetargetLoadNotifications(nsIRequest *request)
|
|
|
|
|
{
|
|
|
|
|
// we are going to run the downloading of the helper app in our own little docloader / load group context.
|
|
|
|
@ -1377,10 +1400,6 @@ nsresult nsExternalAppHandler::SetUpTempFile(nsIChannel * aChannel)
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rv = mSaver->EnableSha256();
|
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
LOG(("Enabled hashing"));
|
|
|
|
|
|
|
|
|
|
rv = mSaver->SetTarget(mTempFile, false);
|
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
|
@ -1575,18 +1594,25 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest *request, nsISuppo
|
|
|
|
|
|
|
|
|
|
if (alwaysAsk)
|
|
|
|
|
{
|
|
|
|
|
// do this first! make sure we don't try to take an action until the user tells us what they want to do
|
|
|
|
|
// with it...
|
|
|
|
|
mReceivedDispositionInfo = false;
|
|
|
|
|
mKeepRequestAlive = true;
|
|
|
|
|
|
|
|
|
|
// invoke the dialog!!!!! use mWindowContext as the window context parameter for the dialog request
|
|
|
|
|
mDialog = do_CreateInstance( NS_HELPERAPPLAUNCHERDLG_CONTRACTID, &rv );
|
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
|
|
// this will create a reference cycle (the dialog holds a reference to us as
|
|
|
|
|
// nsIHelperAppLauncher), which will be broken in Cancel or CreateTransfer.
|
|
|
|
|
// nsIHelperAppLauncher), which will be broken in Cancel or
|
|
|
|
|
// CreateProgressListener.
|
|
|
|
|
rv = mDialog->Show( this, mWindowContext, mReason );
|
|
|
|
|
|
|
|
|
|
// what do we do if the dialog failed? I guess we should call Cancel and abort the load....
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
mReceivedDispositionInfo = true; // no need to wait for a response from the user
|
|
|
|
|
|
|
|
|
|
// We need to do the save/open immediately, then.
|
|
|
|
|
#ifdef XP_WIN
|
|
|
|
@ -1631,8 +1657,8 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest *request, nsISuppo
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert error info into proper message text and send OnStatusChange
|
|
|
|
|
// notification to the dialog progress listener or nsITransfer implementation.
|
|
|
|
|
// Convert error info into proper message text and send OnStatusChange notification
|
|
|
|
|
// to the web progress listener.
|
|
|
|
|
void nsExternalAppHandler::SendStatusChange(ErrorType type, nsresult rv, nsIRequest *aRequest, const nsAFlatString &path)
|
|
|
|
|
{
|
|
|
|
|
nsAutoString msgId;
|
|
|
|
@ -1698,7 +1724,7 @@ void nsExternalAppHandler::SendStatusChange(ErrorType type, nsresult rv, nsIRequ
|
|
|
|
|
}
|
|
|
|
|
PR_LOG(nsExternalHelperAppService::mLog, PR_LOG_ERROR,
|
|
|
|
|
("Error: %s, type=%i, listener=0x%p, rv=0x%08X\n",
|
|
|
|
|
NS_LossyConvertUTF16toASCII(msgId).get(), type, mDialogProgressListener.get(), rv));
|
|
|
|
|
NS_LossyConvertUTF16toASCII(msgId).get(), type, mWebProgressListener.get(), rv));
|
|
|
|
|
PR_LOG(nsExternalHelperAppService::mLog, PR_LOG_ERROR,
|
|
|
|
|
(" path='%s'\n", NS_ConvertUTF16toUTF8(path).get()));
|
|
|
|
|
|
|
|
|
@ -1714,12 +1740,10 @@ void nsExternalAppHandler::SendStatusChange(ErrorType type, nsresult rv, nsIRequ
|
|
|
|
|
const PRUnichar *strings[] = { path.get() };
|
|
|
|
|
if(NS_SUCCEEDED(bundle->FormatStringFromName(msgId.get(), strings, 1, getter_Copies(msgText))))
|
|
|
|
|
{
|
|
|
|
|
if (mDialogProgressListener)
|
|
|
|
|
if (mWebProgressListener)
|
|
|
|
|
{
|
|
|
|
|
// We have a listener, let it handle the error.
|
|
|
|
|
mDialogProgressListener->OnStatusChange(nullptr, (type == kReadError) ? aRequest : nullptr, rv, msgText);
|
|
|
|
|
} else if (mTransfer) {
|
|
|
|
|
mTransfer->OnStatusChange(nullptr, (type == kReadError) ? aRequest : nullptr, rv, msgText);
|
|
|
|
|
mWebProgressListener->OnStatusChange(nullptr, (type == kReadError) ? aRequest : nullptr, rv, msgText);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
if (XRE_GetProcessType() == GeckoProcessType_Default) {
|
|
|
|
@ -1760,10 +1784,11 @@ nsExternalAppHandler::OnDataAvailable(nsIRequest *request, nsISupports * aCtxt,
|
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
|
|
|
{
|
|
|
|
|
// Send progress notification.
|
|
|
|
|
if (mTransfer) {
|
|
|
|
|
mTransfer->OnProgressChange64(nullptr, request, mProgress,
|
|
|
|
|
mContentLength, mProgress,
|
|
|
|
|
mContentLength);
|
|
|
|
|
if (mWebProgressListener)
|
|
|
|
|
{
|
|
|
|
|
mWebProgressListener->OnProgressChange64(nullptr, request, mProgress,
|
|
|
|
|
mContentLength, mProgress,
|
|
|
|
|
mContentLength);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
@ -1786,6 +1811,9 @@ NS_IMETHODIMP nsExternalAppHandler::OnStopRequest(nsIRequest *request, nsISuppor
|
|
|
|
|
{
|
|
|
|
|
mStopRequestIssued = true;
|
|
|
|
|
|
|
|
|
|
if (!mKeepRequestAlive)
|
|
|
|
|
mRequest = nullptr;
|
|
|
|
|
|
|
|
|
|
// Cancel if the request did not complete successfully.
|
|
|
|
|
if (!mCanceled && NS_FAILED(aStatus))
|
|
|
|
|
{
|
|
|
|
@ -1816,10 +1844,6 @@ NS_IMETHODIMP
|
|
|
|
|
nsExternalAppHandler::OnSaveComplete(nsIBackgroundFileSaver *aSaver,
|
|
|
|
|
nsresult aStatus)
|
|
|
|
|
{
|
|
|
|
|
// Save the hash
|
|
|
|
|
nsresult rv = mSaver->GetSha256Hash(mHash);
|
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
|
|
// Free the reference that the saver keeps on us.
|
|
|
|
|
mSaver = nullptr;
|
|
|
|
|
|
|
|
|
@ -1835,48 +1859,57 @@ nsExternalAppHandler::OnSaveComplete(nsIBackgroundFileSaver *aSaver,
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Notify the transfer object that we are done if the user has chosen an
|
|
|
|
|
// action. If the user hasn't chosen an action and InitializeDownload hasn't
|
|
|
|
|
// been called, the progress listener (nsITransfer) will be notified in
|
|
|
|
|
// CreateTransfer.
|
|
|
|
|
if (mTransfer) {
|
|
|
|
|
rv = NotifyTransfer();
|
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nsresult nsExternalAppHandler::NotifyTransfer()
|
|
|
|
|
{
|
|
|
|
|
MOZ_ASSERT(NS_IsMainThread(), "Must notify on main thread");
|
|
|
|
|
MOZ_ASSERT(!mCanceled, "Can't notify if canceled or action "
|
|
|
|
|
"hasn't been chosen");
|
|
|
|
|
MOZ_ASSERT(mTransfer, "We must have an nsITransfer");
|
|
|
|
|
|
|
|
|
|
LOG(("Notifying progress listener"));
|
|
|
|
|
|
|
|
|
|
nsresult rv = mTransfer->SetSha256Hash(mHash);
|
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
|
|
rv = mTransfer->OnProgressChange64(nullptr, nullptr, mProgress,
|
|
|
|
|
mContentLength, mProgress, mContentLength);
|
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
|
|
rv = mTransfer->OnStateChange(nullptr, nullptr,
|
|
|
|
|
nsIWebProgressListener::STATE_STOP |
|
|
|
|
|
nsIWebProgressListener::STATE_IS_REQUEST |
|
|
|
|
|
nsIWebProgressListener::STATE_IS_NETWORK, NS_OK);
|
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
// Do what the user asked for
|
|
|
|
|
ExecuteDesiredAction();
|
|
|
|
|
|
|
|
|
|
// This nsITransfer object holds a reference to us (we are its observer), so
|
|
|
|
|
// we need to release the reference to break a reference cycle (and therefore
|
|
|
|
|
// to prevent leaking)
|
|
|
|
|
mTransfer = nullptr;
|
|
|
|
|
mWebProgressListener = nullptr;
|
|
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nsresult nsExternalAppHandler::ExecuteDesiredAction()
|
|
|
|
|
{
|
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
|
if (mProgressListenerInitialized && !mCanceled)
|
|
|
|
|
{
|
|
|
|
|
rv = MoveFile(mFinalFileDestination);
|
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
|
|
|
{
|
|
|
|
|
nsHandlerInfoAction action = nsIMIMEInfo::saveToDisk;
|
|
|
|
|
mMimeInfo->GetPreferredAction(&action);
|
|
|
|
|
if (action == nsIMIMEInfo::useHelperApp ||
|
|
|
|
|
action == nsIMIMEInfo::useSystemDefault)
|
|
|
|
|
{
|
|
|
|
|
rv = OpenWithApplication();
|
|
|
|
|
}
|
|
|
|
|
else if(action == nsIMIMEInfo::saveToDisk)
|
|
|
|
|
{
|
|
|
|
|
mExtProtSvc->FixFilePermissions(mFinalFileDestination);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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->OnProgressChange64(nullptr, nullptr, mProgress, mContentLength, mProgress, mContentLength);
|
|
|
|
|
}
|
|
|
|
|
mWebProgressListener->OnStateChange(nullptr, nullptr,
|
|
|
|
|
nsIWebProgressListener::STATE_STOP |
|
|
|
|
|
nsIWebProgressListener::STATE_IS_REQUEST |
|
|
|
|
|
nsIWebProgressListener::STATE_IS_NETWORK, NS_OK);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsExternalAppHandler::GetMIMEInfo(nsIMIMEInfo ** aMIMEInfo)
|
|
|
|
|
{
|
|
|
|
|
*aMIMEInfo = mMimeInfo;
|
|
|
|
@ -1904,14 +1937,14 @@ nsresult nsExternalAppHandler::InitializeDownload(nsITransfer* aTransfer)
|
|
|
|
|
|
|
|
|
|
nsCOMPtr<nsIURI> target;
|
|
|
|
|
rv = NS_NewFileURI(getter_AddRefs(target), mFinalFileDestination);
|
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
|
|
nsCOMPtr<nsIChannel> channel = do_QueryInterface(mRequest);
|
|
|
|
|
|
|
|
|
|
rv = aTransfer->Init(mSourceUrl, target, EmptyString(),
|
|
|
|
|
mMimeInfo, mTimeDownloadStarted, mTempFile, this,
|
|
|
|
|
channel && NS_UsePrivateBrowsing(channel));
|
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
|
|
// Now let's add the download to history
|
|
|
|
|
nsCOMPtr<nsIDownloadHistory> dh(do_GetService(NS_DOWNLOADHISTORY_CONTRACTID));
|
|
|
|
@ -1930,38 +1963,31 @@ nsresult nsExternalAppHandler::InitializeDownload(nsITransfer* aTransfer)
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nsresult nsExternalAppHandler::CreateTransfer()
|
|
|
|
|
nsresult nsExternalAppHandler::CreateProgressListener()
|
|
|
|
|
{
|
|
|
|
|
// We are back from the helper app dialog (where the user chooses to save or
|
|
|
|
|
// open), but we aren't done processing the load. in this case, throw up a
|
|
|
|
|
// progress dialog so the user can see what's going on.
|
|
|
|
|
// we are back from the helper app dialog (where the user chooses to save or open), but we aren't
|
|
|
|
|
// done processing the load. in this case, throw up a progress dialog so the user can see what's going on...
|
|
|
|
|
// Also, release our reference to mDialog. We don't need it anymore, and we
|
|
|
|
|
// need to break the reference cycle.
|
|
|
|
|
mDialog = nullptr;
|
|
|
|
|
if (!mDialogProgressListener) {
|
|
|
|
|
NS_WARNING("The dialog should nullify the dialog progress listener");
|
|
|
|
|
}
|
|
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
|
|
// We must be able to create an nsITransfer object. If not, it doesn't matter
|
|
|
|
|
// much that we can't launch the helper application or save to disk.
|
|
|
|
|
mTransfer = do_CreateInstance(NS_TRANSFER_CONTRACTID, &rv);
|
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
rv = InitializeDownload(mTransfer);
|
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
nsCOMPtr<nsITransfer> tr = do_CreateInstance(NS_TRANSFER_CONTRACTID, &rv);
|
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
|
|
|
InitializeDownload(tr);
|
|
|
|
|
|
|
|
|
|
rv = mTransfer->OnStateChange(nullptr, mRequest,
|
|
|
|
|
nsIWebProgressListener::STATE_START |
|
|
|
|
|
nsIWebProgressListener::STATE_IS_REQUEST |
|
|
|
|
|
nsIWebProgressListener::STATE_IS_NETWORK, NS_OK);
|
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
if (tr)
|
|
|
|
|
tr->OnStateChange(nullptr, mRequest, nsIWebProgressListener::STATE_START |
|
|
|
|
|
nsIWebProgressListener::STATE_IS_REQUEST |
|
|
|
|
|
nsIWebProgressListener::STATE_IS_NETWORK, NS_OK);
|
|
|
|
|
|
|
|
|
|
// While we were bringing up the progress dialog, we actually finished
|
|
|
|
|
// processing the url. If that's the case then mStopRequestIssued will be
|
|
|
|
|
// true and OnSaveComplete has been called.
|
|
|
|
|
if (mStopRequestIssued && !mSaver) {
|
|
|
|
|
return NotifyTransfer();
|
|
|
|
|
}
|
|
|
|
|
// note we might not have a listener here if the QI() failed, or if
|
|
|
|
|
// there is no nsITransfer object, but we still call
|
|
|
|
|
// SetWebProgressListener() to make sure our progress state is sane
|
|
|
|
|
// NOTE: This will set up a reference cycle (this nsITransfer has us set up as
|
|
|
|
|
// its observer). This cycle will be broken in Cancel, CloseProgressWindow or
|
|
|
|
|
// OnStopRequest.
|
|
|
|
|
SetWebProgressListener(tr);
|
|
|
|
|
|
|
|
|
|
mRequest = nullptr;
|
|
|
|
|
|
|
|
|
@ -2023,9 +2049,68 @@ void nsExternalAppHandler::RequestSaveDestination(const nsAFlatString &aDefaultF
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nsresult nsExternalAppHandler::MoveFile(nsIFile * aNewFileLocation)
|
|
|
|
|
{
|
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
|
NS_ASSERTION(mStopRequestIssued, "uhoh, how did we get here if we aren't done getting data?");
|
|
|
|
|
|
|
|
|
|
// if the on stop request was actually issued then it's now time to actually perform the file move....
|
|
|
|
|
if (mStopRequestIssued && aNewFileLocation)
|
|
|
|
|
{
|
|
|
|
|
// Unfortunately, MoveTo will fail if a file already exists at the user specified location....
|
|
|
|
|
// but the user has told us, this is where they want the file! (when we threw up the save to file dialog,
|
|
|
|
|
// it told them the file already exists and do they wish to over write it. So it should be okay to delete
|
|
|
|
|
// fileToUse if it already exists.
|
|
|
|
|
bool equalToTempFile = false;
|
|
|
|
|
bool filetoUseAlreadyExists = false;
|
|
|
|
|
aNewFileLocation->Equals(mTempFile, &equalToTempFile);
|
|
|
|
|
aNewFileLocation->Exists(&filetoUseAlreadyExists);
|
|
|
|
|
if (filetoUseAlreadyExists && !equalToTempFile)
|
|
|
|
|
aNewFileLocation->Remove(false);
|
|
|
|
|
|
|
|
|
|
// extract the new leaf name from the file location
|
|
|
|
|
nsAutoString fileName;
|
|
|
|
|
aNewFileLocation->GetLeafName(fileName);
|
|
|
|
|
nsCOMPtr<nsIFile> directoryLocation;
|
|
|
|
|
rv = aNewFileLocation->GetParent(getter_AddRefs(directoryLocation));
|
|
|
|
|
if (directoryLocation)
|
|
|
|
|
{
|
|
|
|
|
rv = mTempFile->MoveTo(directoryLocation, fileName);
|
|
|
|
|
}
|
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
|
{
|
|
|
|
|
// Send error notification.
|
|
|
|
|
nsAutoString path;
|
|
|
|
|
aNewFileLocation->GetPath(path);
|
|
|
|
|
SendStatusChange(kWriteError, rv, nullptr, path);
|
|
|
|
|
Cancel(rv); // Cancel (and clean up temp file).
|
|
|
|
|
}
|
|
|
|
|
#if defined(XP_OS2)
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// tag the file with its source URI
|
|
|
|
|
nsCOMPtr<nsILocalFileOS2> localFileOS2 = do_QueryInterface(aNewFileLocation);
|
|
|
|
|
if (localFileOS2)
|
|
|
|
|
{
|
|
|
|
|
nsAutoCString url;
|
|
|
|
|
mSourceUrl->GetSpec(url);
|
|
|
|
|
localFileOS2->SetFileSource(url);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SaveToDisk should only be called by the helper app dialog which allows
|
|
|
|
|
// the user to say launch with application or save to disk. It doesn't actually
|
|
|
|
|
// perform the save, it just prompts for the destination file name.
|
|
|
|
|
// perform the save, it just prompts for the destination file name. The actual save
|
|
|
|
|
// won't happen until we are done downloading the content and are sure we've
|
|
|
|
|
// shown a progress dialog. This was done to simplify the
|
|
|
|
|
// logic that was showing up in this method. Internal callers who actually want
|
|
|
|
|
// to preform the save should call ::MoveFile
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsExternalAppHandler::SaveToDisk(nsIFile * aNewFileLocation, bool aRememberThisPreference)
|
|
|
|
|
{
|
|
|
|
|
if (mCanceled)
|
|
|
|
@ -2033,6 +2118,9 @@ NS_IMETHODIMP nsExternalAppHandler::SaveToDisk(nsIFile * aNewFileLocation, bool
|
|
|
|
|
|
|
|
|
|
mMimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk);
|
|
|
|
|
|
|
|
|
|
// The helper app dialog has told us what to do.
|
|
|
|
|
mReceivedDispositionInfo = true;
|
|
|
|
|
|
|
|
|
|
if (!aNewFileLocation) {
|
|
|
|
|
if (mSuggestedFileName.IsEmpty())
|
|
|
|
|
RequestSaveDestination(mTempLeafName, mTempFileExtension);
|
|
|
|
@ -2055,9 +2143,6 @@ NS_IMETHODIMP nsExternalAppHandler::SaveToDisk(nsIFile * aNewFileLocation, bool
|
|
|
|
|
}
|
|
|
|
|
nsresult nsExternalAppHandler::ContinueSave(nsIFile * aNewFileLocation)
|
|
|
|
|
{
|
|
|
|
|
if (mCanceled)
|
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
|
|
NS_PRECONDITION(aNewFileLocation, "Must be called with a non-null file");
|
|
|
|
|
|
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
@ -2065,10 +2150,9 @@ nsresult nsExternalAppHandler::ContinueSave(nsIFile * aNewFileLocation)
|
|
|
|
|
mFinalFileDestination = do_QueryInterface(fileToUse);
|
|
|
|
|
|
|
|
|
|
// Move what we have in the final directory, but append .part
|
|
|
|
|
// to it, to indicate that it's unfinished. Do not call SetTarget on the
|
|
|
|
|
// saver if we are done (Finish has been called) but OnSaverComplete has not
|
|
|
|
|
// been called.
|
|
|
|
|
if (mFinalFileDestination && mSaver && !mStopRequestIssued)
|
|
|
|
|
// to it, to indicate that it's unfinished.
|
|
|
|
|
// do not do that if we're already done
|
|
|
|
|
if (mFinalFileDestination && !mStopRequestIssued)
|
|
|
|
|
{
|
|
|
|
|
nsCOMPtr<nsIFile> movedFile;
|
|
|
|
|
mFinalFileDestination->Clone(getter_AddRefs(movedFile));
|
|
|
|
@ -2079,22 +2163,24 @@ nsresult nsExternalAppHandler::ContinueSave(nsIFile * aNewFileLocation)
|
|
|
|
|
name.AppendLiteral(".part");
|
|
|
|
|
movedFile->SetLeafName(name);
|
|
|
|
|
|
|
|
|
|
rv = mSaver->SetTarget(movedFile, true);
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
|
nsAutoString path;
|
|
|
|
|
mTempFile->GetPath(path);
|
|
|
|
|
SendStatusChange(kWriteError, rv, nullptr, path);
|
|
|
|
|
Cancel(rv);
|
|
|
|
|
return NS_OK;
|
|
|
|
|
if (mSaver)
|
|
|
|
|
{
|
|
|
|
|
rv = mSaver->SetTarget(movedFile, true);
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
|
nsAutoString path;
|
|
|
|
|
mTempFile->GetPath(path);
|
|
|
|
|
SendStatusChange(kWriteError, rv, nullptr, path);
|
|
|
|
|
Cancel(rv);
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mTempFile = movedFile;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The helper app dialog has told us what to do and we have a final file
|
|
|
|
|
// destination.
|
|
|
|
|
CreateTransfer();
|
|
|
|
|
if (!mProgressListenerInitialized)
|
|
|
|
|
CreateProgressListener();
|
|
|
|
|
|
|
|
|
|
// now that the user has chosen the file location to save to, it's okay to fire the refresh tag
|
|
|
|
|
// if there is one. We don't want to do this before the save as dialog goes away because this dialog
|
|
|
|
@ -2106,9 +2192,68 @@ nsresult nsExternalAppHandler::ContinueSave(nsIFile * aNewFileLocation)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// LaunchWithApplication should only be called by the helper app dialog which
|
|
|
|
|
// allows the user to say launch with application or save to disk. It doesn't
|
|
|
|
|
// actually perform launch with application.
|
|
|
|
|
nsresult nsExternalAppHandler::OpenWithApplication()
|
|
|
|
|
{
|
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
|
if (mCanceled)
|
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
|
|
// we only should have gotten here if the on stop request had been fired already.
|
|
|
|
|
|
|
|
|
|
NS_ASSERTION(mStopRequestIssued, "uhoh, how did we get here if we aren't done getting data?");
|
|
|
|
|
// if a stop request was already issued then proceed with launching the application.
|
|
|
|
|
if (mStopRequestIssued)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
// Note for the default value:
|
|
|
|
|
// 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.
|
|
|
|
|
bool deleteTempFileOnExit =
|
|
|
|
|
Preferences::GetBool("browser.helperApps.deleteTempFileOnExit",
|
|
|
|
|
#if !defined(XP_MACOSX)
|
|
|
|
|
true);
|
|
|
|
|
#else
|
|
|
|
|
false);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// See whether the channel has been opened in private browsing mode
|
|
|
|
|
NS_ASSERTION(mRequest, "This should never be called with a null request");
|
|
|
|
|
nsCOMPtr<nsIChannel> channel = do_QueryInterface(mRequest);
|
|
|
|
|
bool inPrivateBrowsing = channel && NS_UsePrivateBrowsing(channel);
|
|
|
|
|
|
|
|
|
|
// make the tmp file readonly so users won't edit it and lose the changes
|
|
|
|
|
// only if we're going to delete the file
|
|
|
|
|
if (deleteTempFileOnExit || inPrivateBrowsing)
|
|
|
|
|
mFinalFileDestination->SetPermissions(0400);
|
|
|
|
|
|
|
|
|
|
rv = mMimeInfo->LaunchWithFile(mFinalFileDestination);
|
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
|
{
|
|
|
|
|
// Send error notification.
|
|
|
|
|
nsAutoString path;
|
|
|
|
|
mFinalFileDestination->GetPath(path);
|
|
|
|
|
SendStatusChange(kLaunchError, rv, nullptr, path);
|
|
|
|
|
Cancel(rv); // Cancel, and clean up temp file.
|
|
|
|
|
}
|
|
|
|
|
// Always schedule files to be deleted at the end of the private browsing
|
|
|
|
|
// mode, regardless of the value of the pref.
|
|
|
|
|
else if (deleteTempFileOnExit) {
|
|
|
|
|
mExtProtSvc->DeleteTemporaryFileOnExit(mFinalFileDestination);
|
|
|
|
|
}
|
|
|
|
|
else if (inPrivateBrowsing) {
|
|
|
|
|
mExtProtSvc->DeleteTemporaryPrivateFileWhenPossible(mFinalFileDestination);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LaunchWithApplication should only be called by the helper app dialog which allows
|
|
|
|
|
// the user to say launch with application or save to disk. It doesn't actually
|
|
|
|
|
// perform launch with application. That won't happen until we are done downloading
|
|
|
|
|
// the content and are sure we've shown a progress dialog. This was done to simplify the
|
|
|
|
|
// logic that was showing up in this method.
|
|
|
|
|
NS_IMETHODIMP nsExternalAppHandler::LaunchWithApplication(nsIFile * aApplication, bool aRememberThisPreference)
|
|
|
|
|
{
|
|
|
|
|
if (mCanceled)
|
|
|
|
@ -2117,6 +2262,7 @@ NS_IMETHODIMP nsExternalAppHandler::LaunchWithApplication(nsIFile * aApplication
|
|
|
|
|
// user has chosen to launch using an application, fire any refresh tags now...
|
|
|
|
|
ProcessAnyRefreshTags();
|
|
|
|
|
|
|
|
|
|
mReceivedDispositionInfo = true;
|
|
|
|
|
if (mMimeInfo && aApplication) {
|
|
|
|
|
PlatformLocalHandlerApp_t *handlerApp =
|
|
|
|
|
new PlatformLocalHandlerApp_t(EmptyString(), aApplication);
|
|
|
|
@ -2146,13 +2292,12 @@ NS_IMETHODIMP nsExternalAppHandler::LaunchWithApplication(nsIFile * aApplication
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now that the user has elected to launch the downloaded file with a helper
|
|
|
|
|
// app, we're justified in removing the 'salted' name. We'll rename to what
|
|
|
|
|
// was specified in mSuggestedFileName after the download is done prior to
|
|
|
|
|
// launching the helper app. So that any existing file of that name won't be
|
|
|
|
|
// overwritten we call CreateUnique(). Also note that we use the same
|
|
|
|
|
// directory as originally downloaded so nsDownload can rename in place
|
|
|
|
|
// later.
|
|
|
|
|
// Now that the user has elected to launch the downloaded file with a helper app, we're justified in
|
|
|
|
|
// removing the 'salted' name. We'll rename to what was specified in mSuggestedFileName after the
|
|
|
|
|
// download is done prior to launching the helper app. So that any existing file of that name won't
|
|
|
|
|
// be overwritten we call CreateUnique() before calling MoveFile(). Also note that we use the same
|
|
|
|
|
// directory as originally downloaded to so that MoveFile() just does an in place rename.
|
|
|
|
|
|
|
|
|
|
nsCOMPtr<nsIFile> fileToUse;
|
|
|
|
|
(void) GetDownloadDirectory(getter_AddRefs(fileToUse));
|
|
|
|
|
|
|
|
|
@ -2173,7 +2318,8 @@ NS_IMETHODIMP nsExternalAppHandler::LaunchWithApplication(nsIFile * aApplication
|
|
|
|
|
{
|
|
|
|
|
mFinalFileDestination = do_QueryInterface(fileToUse);
|
|
|
|
|
// launch the progress window now that the user has picked the desired action.
|
|
|
|
|
CreateTransfer();
|
|
|
|
|
if (!mProgressListenerInitialized)
|
|
|
|
|
CreateProgressListener();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
@ -2205,8 +2351,7 @@ NS_IMETHODIMP nsExternalAppHandler::Cancel(nsresult aReason)
|
|
|
|
|
|
|
|
|
|
// Release the listener, to break the reference cycle with it (we are the
|
|
|
|
|
// observer of the listener).
|
|
|
|
|
mDialogProgressListener = nullptr;
|
|
|
|
|
mTransfer = nullptr;
|
|
|
|
|
mWebProgressListener = nullptr;
|
|
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|