/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Conrad Carlen <ccarlen@netscape.com>
*
* Adapted for epiphany by Marco Pesenti Gritti <marco@gnome.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK *****
*
* $Id$
*/
#include "MozDownload.h"
#include "mozilla-download.h"
#include "nsIExternalHelperAppService.h"
#include "nsDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsIRequest.h"
#include "netCore.h"
#include "nsIObserver.h"
const char* const persistContractID = "@mozilla.org/embedding/browser/nsWebBrowserPersist;1";
MozDownload::MozDownload() :
mGotFirstStateChange(false), mIsNetworkTransfer(false),
mUserCanceled(false),
mStatus(NS_OK),
mEmbedPersist(nsnull),
mDownloadState(EPHY_DOWNLOAD_DOWNLOADING)
{
}
MozDownload::~MozDownload()
{
}
NS_IMPL_ISUPPORTS2(MozDownload, nsIDownload, nsIWebProgressListener)
NS_IMETHODIMP
MozDownload::InitForEmbed (nsIURI *aSource, nsILocalFile *aTarget, const PRUnichar *aDisplayName,
nsIMIMEInfo *aMIMEInfo, PRInt64 startTime, nsIWebBrowserPersist *aPersist,
MozillaEmbedPersist *aEmbedPersist)
{
mEmbedPersist = aEmbedPersist;
return Init (aSource, aTarget, aDisplayName, aMIMEInfo, startTime, aPersist);
}
/* void init (in nsIURI aSource, in nsILocalFile aTarget, in wstring aDisplayName, in nsIMIMEInfo aMIMEInfo, in long long startTime, in nsIWebBrowserPersist aPersist); */
NS_IMETHODIMP
MozDownload::Init(nsIURI *aSource, nsILocalFile *aTarget, const PRUnichar *aDisplayName,
nsIMIMEInfo *aMIMEInfo, PRInt64 startTime, nsIWebBrowserPersist *aPersist)
{
PRBool addToView = PR_TRUE;
if (mEmbedPersist)
{
EmbedPersistFlags flags;
flags = ephy_embed_persist_get_flags (EPHY_EMBED_PERSIST (mEmbedPersist));
addToView = !(flags & EMBED_PERSIST_NO_VIEW);
}
mSource = aSource;
mDestination = aTarget;
mStartTime = startTime;
mTotalProgress = 0;
mCurrentProgress = 0;
mPercentComplete = 0;
mInterval = 4000; /* in ms */
mLastUpdate = mStartTime;
if (aPersist)
{
mWebPersist = aPersist;
aPersist->SetProgressListener(this);
}
if (addToView)
{
DownloaderView *dview;
dview = EPHY_DOWNLOADER_VIEW
(ephy_embed_shell_get_downloader_view (embed_shell));
mEphyDownload = mozilla_download_new ();
MOZILLA_DOWNLOAD (mEphyDownload)->moz_download = this;
downloader_view_add_download (dview, mEphyDownload);
}
else
{
mEphyDownload = nsnull;
}
return NS_OK;
}
NS_IMETHODIMP
MozDownload::GetSource(nsIURI **aSource)
{
NS_ENSURE_ARG_POINTER(aSource);
NS_IF_ADDREF(*aSource = mSource);
return NS_OK;
}
NS_IMETHODIMP
MozDownload::GetTarget(nsILocalFile **aTarget)
{
NS_ENSURE_ARG_POINTER(aTarget);
NS_IF_ADDREF(*aTarget = mDestination);
return NS_OK;
}
NS_IMETHODIMP
MozDownload::GetPersist(nsIWebBrowserPersist **aPersist)
{
NS_ENSURE_ARG_POINTER(aPersist);
NS_IF_ADDREF(*aPersist = mWebPersist);
return NS_OK;
}
NS_IMETHODIMP
MozDownload::GetPercentComplete(PRInt32 *aPercentComplete)
{
NS_ENSURE_ARG_POINTER(aPercentComplete);
*aPercentComplete = mPercentComplete;
return NS_OK;
}
NS_IMETHODIMP
MozDownload::GetTotalProgress(PRInt32 *aTotalProgress)
{
NS_ENSURE_ARG_POINTER(aTotalProgress);
*aTotalProgress = mTotalProgress;
return NS_OK;
}
NS_IMETHODIMP
MozDownload::GetCurrentProgress(PRInt32 *aCurrentProgress)
{
NS_ENSURE_ARG_POINTER(aCurrentProgress);
*aCurrentProgress = mCurrentProgress;
return NS_OK;
}
NS_IMETHODIMP
MozDownload::GetState(EphyDownloadState *aDownloadState)
{
NS_ENSURE_ARG_POINTER(aDownloadState);
*aDownloadState = mDownloadState;
return NS_OK;
}
NS_IMETHODIMP
MozDownload::GetDisplayName(PRUnichar * *aDisplayName)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
MozDownload::SetDisplayName(const PRUnichar * aDisplayName)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
MozDownload::GetStartTime(PRInt64 *aStartTime)
{
NS_ENSURE_ARG_POINTER(aStartTime);
*aStartTime = mStartTime;
return NS_OK;
}
NS_IMETHODIMP
MozDownload::GetElapsedTime(PRInt64 *aElapsedTime)
{
NS_ENSURE_ARG_POINTER(aElapsedTime);
*aElapsedTime = PR_Now() - mStartTime;
return NS_OK;
}
NS_IMETHODIMP
MozDownload::GetMIMEInfo(nsIMIMEInfo **aMIMEInfo)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
MozDownload::GetListener(nsIWebProgressListener **aListener)
{
NS_ENSURE_ARG_POINTER(aListener);
NS_IF_ADDREF(*aListener = (nsIWebProgressListener *)this);
return NS_OK;
}
NS_IMETHODIMP
MozDownload::SetListener(nsIWebProgressListener *aListener)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
MozDownload::GetObserver(nsIObserver **aObserver)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
MozDownload::SetObserver(nsIObserver *aObserver)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
MozDownload::OnStateChange (nsIWebProgress *aWebProgress, nsIRequest *aRequest,
PRUint32 aStateFlags, nsresult aStatus)
{
/* For a file download via the external helper app service, we will never get a start
notification. The helper app service has gotten that notification before it created us. */
if (!mGotFirstStateChange)
{
mIsNetworkTransfer = ((aStateFlags & STATE_IS_NETWORK) != 0);
mGotFirstStateChange = PR_TRUE;
}
if (NS_FAILED(aStatus) && NS_SUCCEEDED(mStatus))
mStatus = aStatus;
/* We will get this even in the event of a cancel */
if ((aStateFlags & STATE_STOP) && (!mIsNetworkTransfer || (aStateFlags & STATE_IS_NETWORK)))
{
/* Keep us alive */
nsCOMPtr<nsIDownload> kungFuDeathGrip(this);
mDownloadState = NS_SUCCEEDED (aStatus) ? EPHY_DOWNLOAD_COMPLETED : EPHY_DOWNLOAD_FAILED;
if (mEphyDownload)
{
g_signal_emit_by_name (mEphyDownload, "changed");
}
if (mWebPersist)
{
mWebPersist->SetProgressListener(nsnull);
mWebPersist = nsnull;
}
if (mEmbedPersist)
{
if (NS_SUCCEEDED (aStatus))
{
mozilla_embed_persist_completed (mEmbedPersist);
}
else
{
mozilla_embed_persist_cancelled (mEmbedPersist);
}
}
}
return NS_OK;
}
NS_IMETHODIMP
MozDownload::OnProgressChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest,
PRInt32 aCurSelfProgress, PRInt32 aMaxSelfProgress,
PRInt32 aCurTotalProgress, PRInt32 aMaxTotalProgress)
{
PRInt64 now = PR_Now ();
if ((now - mLastUpdate < mInterval) &&
(aMaxTotalProgress != -1) &&
(aCurTotalProgress < aMaxTotalProgress))
return NS_OK;
mLastUpdate = now;
if (mUserCanceled)
{
if (aRequest)
{
aRequest->Cancel(NS_BINDING_ABORTED);
}
mUserCanceled = false;
}
if (aMaxTotalProgress == -1)
{
mPercentComplete = -1;
}
else
{
mPercentComplete = (PRInt32)(((float)aCurTotalProgress / (float)aMaxTotalProgress) * 100.0 + 0.5);
}
mTotalProgress = aMaxTotalProgress;
mCurrentProgress = aCurTotalProgress;
if (mEphyDownload)
{
g_signal_emit_by_name (mEphyDownload, "changed");
}
return NS_OK;
}
NS_IMETHODIMP
MozDownload::OnLocationChange (nsIWebProgress *aWebProgress, nsIRequest *aRequest, nsIURI *location)
{
return NS_OK;
}
NS_IMETHODIMP
MozDownload::OnStatusChange (nsIWebProgress *aWebProgress, nsIRequest *aRequest,
nsresult aStatus, const PRUnichar *aMessage)
{
return NS_OK;
}
NS_IMETHODIMP
MozDownload::OnSecurityChange (nsIWebProgress *aWebProgress, nsIRequest *aRequest, PRUint32 state)
{
return NS_OK;
}
void
MozDownload::Cancel()
{
mUserCanceled = true;
/* nsWebBrowserPersist does the right thing: After canceling, next time through
OnStateChange(), aStatus != NS_OK. This isn't the case with nsExternalHelperAppService.*/
if (!mWebPersist)
mStatus = NS_ERROR_ABORT;
}
void
MozDownload::Pause()
{
}
void
MozDownload::Resume()
{
}
nsresult InitiateMozillaDownload (nsIDOMDocument *domDocument, nsIURI *sourceURI,
nsILocalFile* inDestFile, const char *contentType,
nsIURI* inOriginalURI, MozillaEmbedPersist *embedPersist,
nsIInputStream *postData, nsISupports *aCacheKey)
{
nsresult rv = NS_OK;
EmbedPersistFlags ephy_flags;
ephy_flags = ephy_embed_persist_get_flags (EPHY_EMBED_PERSIST (embedPersist));
PRBool isHTML = (domDocument && contentType &&
(strcmp (contentType, "text/html") == 0 ||
strcmp (contentType, "text/xml") == 0 ||
strcmp (contentType, "application/xhtml+xml") == 0));
nsCOMPtr<nsIWebBrowserPersist> webPersist = do_CreateInstance(persistContractID, &rv);
if (NS_FAILED(rv)) return rv;
PRInt64 timeNow = PR_Now();
nsAutoString fileDisplayName;
inDestFile->GetLeafName(fileDisplayName);
MozDownload *downloader = new MozDownload ();
/* dlListener attaches to its progress dialog here, which gains ownership */
rv = downloader->InitForEmbed (inOriginalURI, inDestFile, fileDisplayName.get(),
nsnull, timeNow, webPersist, embedPersist);
if (NS_FAILED(rv)) return rv;
PRInt32 flags = nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES;
if (ephy_flags & EMBED_PERSIST_COPY_PAGE)
{
flags |= nsIWebBrowserPersist::PERSIST_FLAGS_FROM_CACHE;
}
webPersist->SetPersistFlags(flags);
if (!isHTML || ephy_flags & EMBED_PERSIST_COPY_PAGE)
{
rv = webPersist->SaveURI (sourceURI, aCacheKey, nsnull,
postData, nsnull, inDestFile);
}
else
{
if (!domDocument) return rv; /* should never happen */
PRInt32 encodingFlags = 0;
nsCOMPtr<nsILocalFile> filesFolder;
if (contentType && strcmp (contentType, "text/plain") == 0)
{
/* Create a local directory in the same dir as our file. It
will hold our associated files. */
filesFolder = do_CreateInstance("@mozilla.org/file/local;1");
nsAutoString unicodePath;
inDestFile->GetPath(unicodePath);
filesFolder->InitWithPath(unicodePath);
nsAutoString leafName;
filesFolder->GetLeafName(leafName);
nsAutoString nameMinusExt(leafName);
PRInt32 index = nameMinusExt.RFind(".");
if (index >= 0)
{
nameMinusExt.Left(nameMinusExt, index);
}
nameMinusExt += NS_LITERAL_STRING(" Files");
filesFolder->SetLeafName(nameMinusExt);
PRBool exists = PR_FALSE;
filesFolder->Exists(&exists);
if (!exists)
{
rv = filesFolder->Create(nsILocalFile::DIRECTORY_TYPE, 0755);
if (NS_FAILED(rv)) return rv;
}
}
else
{
encodingFlags |= nsIWebBrowserPersist::ENCODE_FLAGS_FORMATTED |
nsIWebBrowserPersist::ENCODE_FLAGS_ABSOLUTE_LINKS |
nsIWebBrowserPersist::ENCODE_FLAGS_NOFRAMES_CONTENT;
}
rv = webPersist->SaveDocument (domDocument, inDestFile, filesFolder,
contentType, encodingFlags, 80);
}
return rv;
}