зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 019382e24635 (bug 858234) for Linux mochitest-5 leaks.
This commit is contained in:
Родитель
f69515d8d9
Коммит
b5aaf55a6a
|
@ -2525,13 +2525,6 @@ nsDownload::~nsDownload()
|
|||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsDownload::SetSha256Hash(const nsACString& aHash) {
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must call SetSha256Hash on main thread");
|
||||
// This will be used later to query the application reputation service.
|
||||
mHash = aHash;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef MOZ_ENABLE_GIO
|
||||
static void gio_set_metadata_done(GObject *source_obj, GAsyncResult *res, gpointer user_data)
|
||||
{
|
||||
|
@ -2594,6 +2587,7 @@ nsDownload::SetState(DownloadState aState)
|
|||
#endif
|
||||
case nsIDownloadManager::DOWNLOAD_FINISHED:
|
||||
{
|
||||
// Do what exthandler would have done if necessary
|
||||
nsresult rv = ExecuteDesiredAction();
|
||||
if (NS_FAILED(rv)) {
|
||||
// We've failed to execute the desired action. As a result, we should
|
||||
|
@ -2935,8 +2929,6 @@ nsDownload::OnStateChange(nsIWebProgress *aWebProgress,
|
|||
nsIRequest *aRequest, uint32_t aStateFlags,
|
||||
nsresult aStatus)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must call OnStateChange in main thread");
|
||||
|
||||
// We don't want to lose access to our member variables
|
||||
nsRefPtr<nsDownload> kungFuDeathGrip = this;
|
||||
|
||||
|
@ -3196,12 +3188,10 @@ nsDownload::Finalize()
|
|||
nsresult
|
||||
nsDownload::ExecuteDesiredAction()
|
||||
{
|
||||
// nsExternalHelperAppHandler is the only caller of AddDownload that sets a
|
||||
// tempfile parameter. In this case, execute the desired action according to
|
||||
// the saved mime info.
|
||||
if (!mTempFile) {
|
||||
// If we have a temp file and we have resumed, we have to do what the
|
||||
// external helper app service would have done.
|
||||
if (!mTempFile || !WasResumed())
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// We need to bail if for some reason the temp file got removed
|
||||
bool fileExists;
|
||||
|
|
|
@ -421,11 +421,6 @@ private:
|
|||
enum AutoResume { DONT_RESUME, AUTO_RESUME };
|
||||
AutoResume mAutoResume;
|
||||
|
||||
/**
|
||||
* Stores the SHA-256 hash associated with the downloaded file.
|
||||
*/
|
||||
nsAutoCString mHash;
|
||||
|
||||
friend class nsDownloadManager;
|
||||
};
|
||||
|
||||
|
|
|
@ -16,11 +16,6 @@
|
|||
#define PREF_BDM_SHOWWHENSTARTING "browser.download.manager.showWhenStarting"
|
||||
#define PREF_BDM_FOCUSWHENSTARTING "browser.download.manager.focusWhenStarting"
|
||||
|
||||
// This class only exists because nsDownload cannot inherit from nsITransfer
|
||||
// directly. The reason for this is that nsDownloadManager (incorrectly) keeps
|
||||
// an nsCOMArray of nsDownloads, and nsCOMArray is only intended for use with
|
||||
// abstract classes. Using a concrete class that multiply inherits from classes
|
||||
// deriving from nsISupports will throw ambiguous base class errors.
|
||||
class nsDownloadProxy : public nsITransfer
|
||||
{
|
||||
public:
|
||||
|
@ -148,12 +143,6 @@ public:
|
|||
return mInner->OnSecurityChange(aWebProgress, aRequest, aState);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP SetSha256Hash(const nsACString& aHash)
|
||||
{
|
||||
NS_ENSURE_TRUE(mInner, NS_ERROR_NOT_INITIALIZED);
|
||||
return mInner->SetSha256Hash(aHash);
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIDownload> mInner;
|
||||
};
|
||||
|
|
|
@ -114,12 +114,8 @@ interface nsIDownloadManager : nsISupports {
|
|||
* @param aTempFile The location of a temporary file; i.e. a file in which
|
||||
* the received data will be stored, but which is not
|
||||
* equal to the target file. (will be moved to the real
|
||||
* target by the DownloadManager, when the download is
|
||||
* finished). This will be null for all callers except for
|
||||
* nsExternalHelperAppHandler. Addons should generally pass
|
||||
* null for aTempFile. This will be moved to the real target
|
||||
* by the download manager when the download is finished,
|
||||
* and the action indicated by aMIMEInfo will be executed.
|
||||
* target by the caller, when the download is finished)
|
||||
* May be null.
|
||||
*
|
||||
* @param aCancelable An object that can be used to abort the download.
|
||||
* Must not be null.
|
||||
|
|
|
@ -52,8 +52,7 @@ MockTransfer.prototype = {
|
|||
onRefreshAttempted: function () {},
|
||||
|
||||
/* nsITransfer */
|
||||
init: function() {},
|
||||
setSha256Hash: function() {}
|
||||
init: function () {}
|
||||
};
|
||||
|
||||
// Create an instance of a MockObjectRegisterer whose methods can be used to
|
||||
|
|
|
@ -10,7 +10,7 @@ interface nsICancelable;
|
|||
interface nsIMIMEInfo;
|
||||
interface nsIFile;
|
||||
|
||||
[scriptable, uuid(b1c81100-9d66-11e2-9e96-0800200c9a66)]
|
||||
[scriptable, uuid(08b0b78c-6d7d-4d97-86c9-be5a695813c9)]
|
||||
interface nsITransfer : nsIWebProgressListener2 {
|
||||
|
||||
/**
|
||||
|
@ -57,14 +57,6 @@ interface nsITransfer : nsIWebProgressListener2 {
|
|||
in nsIFile aTempFile,
|
||||
in nsICancelable aCancelable,
|
||||
in boolean aIsPrivate);
|
||||
|
||||
/*
|
||||
* Used to notify the transfer object of the hash of the downloaded file.
|
||||
* Must be called on the main thread, only after the download has finished
|
||||
* successfully.
|
||||
* @param aHash The SHA-256 hash in raw bytes of the downloaded file.
|
||||
*/
|
||||
void setSha256Hash(in ACString aHash);
|
||||
};
|
||||
|
||||
%{C++
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
@ -1156,10 +1157,25 @@ 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;
|
||||
|
||||
nsCOMPtr<nsITransfer> tr = do_CreateInstance(NS_TRANSFER_CONTRACTID, &rv);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
InitializeDownload(tr);
|
||||
|
||||
// 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);
|
||||
if (tr)
|
||||
tr->OnStateChange(nullptr, mRequest, nsIWebProgressListener::STATE_START |
|
||||
nsIWebProgressListener::STATE_IS_REQUEST |
|
||||
nsIWebProgressListener::STATE_IS_NETWORK, NS_OK);
|
||||
|
||||
rv = mTransfer->OnStateChange(nullptr, mRequest,
|
||||
nsIWebProgressListener::STATE_START |
|
||||
nsIWebProgressListener::STATE_IS_REQUEST |
|
||||
nsIWebProgressListener::STATE_IS_NETWORK, NS_OK);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
|
|
@ -282,9 +282,12 @@ protected:
|
|||
bool mShouldCloseWindow;
|
||||
|
||||
/**
|
||||
* True if a stop request has been issued.
|
||||
* have we received information from the user about how they want to
|
||||
* dispose of this content
|
||||
*/
|
||||
bool mReceivedDispositionInfo;
|
||||
bool mStopRequestIssued;
|
||||
bool mProgressListenerInitialized;
|
||||
|
||||
bool mIsFileChannel;
|
||||
|
||||
|
@ -320,11 +323,6 @@ protected:
|
|||
*/
|
||||
nsCOMPtr<nsIBackgroundFileSaver> mSaver;
|
||||
|
||||
/**
|
||||
* Stores the SHA-256 hash associated with the file that we downloaded.
|
||||
*/
|
||||
nsAutoCString mHash;
|
||||
|
||||
/**
|
||||
* Creates the temporary file for the download and an output stream for it.
|
||||
* Upon successful return, both mTempFile and mSaver will be valid.
|
||||
|
@ -338,10 +336,12 @@ protected:
|
|||
*/
|
||||
void RetargetLoadNotifications(nsIRequest *request);
|
||||
/**
|
||||
* Once the user tells us how they want to dispose of the content
|
||||
* create an nsITransfer so they know what's going on...
|
||||
* If the user tells us how they want to dispose of the content and
|
||||
* we still haven't finished downloading while they were deciding,
|
||||
* then create a progress listener of some kind so they know
|
||||
* what's going on...
|
||||
*/
|
||||
nsresult CreateTransfer();
|
||||
nsresult CreateProgressListener();
|
||||
|
||||
|
||||
/*
|
||||
|
@ -380,11 +380,23 @@ protected:
|
|||
*/
|
||||
void ProcessAnyRefreshTags();
|
||||
|
||||
/**
|
||||
* Notify our nsITransfer object that we are done with the download.
|
||||
/**
|
||||
* An internal method used to actually move the temp file to the final
|
||||
* destination once we done receiving data AND have showed the progress dialog
|
||||
*/
|
||||
nsresult NotifyTransfer();
|
||||
|
||||
nsresult MoveFile(nsIFile * aNewFileLocation);
|
||||
/**
|
||||
* An internal method used to actually launch a helper app given the temp file
|
||||
* once we are done receiving data AND have showed the progress dialog.
|
||||
* Uses the application specified in the mime info.
|
||||
*/
|
||||
nsresult OpenWithApplication();
|
||||
|
||||
/**
|
||||
* Helper routine which peaks at the mime action specified by mMimeInfo
|
||||
* and calls either MoveFile or OpenWithApplication
|
||||
*/
|
||||
nsresult ExecuteDesiredAction();
|
||||
/**
|
||||
* Helper routine that searches a pref string for a given mime type
|
||||
*/
|
||||
|
@ -415,17 +427,7 @@ protected:
|
|||
*/
|
||||
nsresult MaybeCloseWindow();
|
||||
|
||||
/**
|
||||
* Set in nsHelperDlgApp.js. This is always null after the user has chosen an
|
||||
* action.
|
||||
*/
|
||||
nsCOMPtr<nsIWebProgressListener2> mDialogProgressListener;
|
||||
/**
|
||||
* Set once the user has chosen an action. This is null after the download
|
||||
* has been canceled or completes.
|
||||
*/
|
||||
nsCOMPtr<nsITransfer> mTransfer;
|
||||
|
||||
nsCOMPtr<nsIWebProgressListener2> mWebProgressListener;
|
||||
nsCOMPtr<nsIChannel> mOriginalChannel; /**< in the case of a redirect, this will be the pre-redirect channel. */
|
||||
nsCOMPtr<nsIHelperAppLauncherDialog> mDialog;
|
||||
|
||||
|
|
|
@ -96,16 +96,14 @@ interface nsIHelperAppLauncher : nsICancelable
|
|||
readonly attribute AString suggestedFileName;
|
||||
|
||||
/**
|
||||
* Saves the final destination of the file. Does not actually perform the
|
||||
* save.
|
||||
* NOTE: This will release the reference to the
|
||||
* nsIHelperAppLauncherDialog.
|
||||
* Called when we want to just save the content to a particular file.
|
||||
* NOTE: This will release the reference to the nsIHelperAppLauncherDialog.
|
||||
* @param aNewFileLocation Location where the content should be saved
|
||||
*/
|
||||
void saveToDisk(in nsIFile aNewFileLocation, in boolean aRememberThisPreference);
|
||||
|
||||
/**
|
||||
* Remembers that aApplication should be used to launch this content. Does
|
||||
* not actually launch the application.
|
||||
* Use aApplication to launch with this content.
|
||||
* NOTE: This will release the reference to the nsIHelperAppLauncherDialog.
|
||||
* @param aApplication nsIFile corresponding to the location of the application to use.
|
||||
* @param aRememberThisPreference TRUE if we should remember this choice.
|
||||
|
@ -127,6 +125,12 @@ interface nsIHelperAppLauncher : nsICancelable
|
|||
*/
|
||||
void setWebProgressListener(in nsIWebProgressListener2 aWebProgressListener);
|
||||
|
||||
/**
|
||||
* when the stand alone progress window actually closes, it calls this method
|
||||
* so we can release any local state...
|
||||
*/
|
||||
void closeProgressWindow();
|
||||
|
||||
/**
|
||||
* The file we are saving to
|
||||
*/
|
||||
|
|
Загрузка…
Ссылка в новой задаче