diff options
Diffstat (limited to 'embed/mozilla')
-rw-r--r-- | embed/mozilla/EphyDownload.cpp | 470 | ||||
-rw-r--r-- | embed/mozilla/EphyDownload.h | 196 | ||||
-rw-r--r-- | embed/mozilla/EphyHeaderSniffer.cpp | 457 | ||||
-rw-r--r-- | embed/mozilla/EphyHeaderSniffer.h | 89 |
4 files changed, 1212 insertions, 0 deletions
diff --git a/embed/mozilla/EphyDownload.cpp b/embed/mozilla/EphyDownload.cpp new file mode 100644 index 000000000..062abc983 --- /dev/null +++ b/embed/mozilla/EphyDownload.cpp @@ -0,0 +1,470 @@ +/* -*- 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> + * + * 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 ***** */ + +#include "EphyDownload.h" + +#include "nsIExternalHelperAppService.h" +//#include "nsILocalFIleMac.h" +#include "nsDirectoryServiceDefs.h" +#include "nsDirectoryServiceUtils.h" +#include "nsIRequest.h" +#include "netCore.h" +#include "nsIObserver.h" + +//#include "UDownloadDisplay.h" +//#include "UMacUnicode.h" + +//#include "UNavServicesDialogs.h" + + +//***************************************************************************** +// EphyDownload +//***************************************************************************** +#pragma mark [EphyDownload] + +//ADownloadProgressView *EphyDownload::sProgressView; + +EphyDownload::EphyDownload() : + mGotFirstStateChange(false), mIsNetworkTransfer(false), + mUserCanceled(false), + mStatus(NS_OK) +{ +} + +EphyDownload::~EphyDownload() +{ +} + +NS_IMPL_ISUPPORTS2(EphyDownload, nsIDownload, nsIWebProgressListener) + +#pragma mark - +#pragma mark [EphyDownload::nsIDownload] + +/* void init (in nsIURI aSource, in nsILocalFile aTarget, in wstring aDisplayName, in nsIMIMEInfo aMIMEInfo, in long long startTime, in nsIWebBrowserPersist aPersist); */ +NS_IMETHODIMP +EphyDownload::Init(nsIURI *aSource, nsILocalFile *aTarget, const PRUnichar *aDisplayName, + nsIMIMEInfo *aMIMEInfo, PRInt64 startTime, nsIWebBrowserPersist *aPersist) +{ + try { + mSource = aSource; + mDestination = aTarget; + mStartTime = startTime; + mPercentComplete = 0; + mInterval = 400000; // ms + mPriorKRate = 0; + mRateChanges = 0; + mRateChangeLimit = 2; + mIsPaused = PR_FALSE; + mStartTime = PR_Now(); + mLastUpdate = mStartTime; + if (aPersist) { + mWebPersist = aPersist; + // We have to break this circular ref when the download is done - + // until nsIWebBrowserPersist supports weak refs - bug #163889. + aPersist->SetProgressListener(this); + } + // UI Rumba + mDownloaderView = EPHY_DOWNLOADER_VIEW (ephy_embed_shell_get_downloader_view + (embed_shell)); + downloader_view_add_download (mDownloaderView, "A", "B", "C", (gpointer)this); + // EnsureProgressView(); + // sProgressView->AddDownloadItem(this); + } + catch (...) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +/* readonly attribute nsIURI source; */ +NS_IMETHODIMP +EphyDownload::GetSource(nsIURI * *aSource) +{ + NS_ENSURE_ARG_POINTER(aSource); + NS_IF_ADDREF(*aSource = mSource); + return NS_OK; +} + +/* readonly attribute nsILocalFile target; */ +NS_IMETHODIMP +EphyDownload::GetTarget(nsILocalFile * *aTarget) +{ + NS_ENSURE_ARG_POINTER(aTarget); + NS_IF_ADDREF(*aTarget = mDestination); + return NS_OK; +} + +/* readonly attribute nsIWebBrowserPersist persist; */ +NS_IMETHODIMP +EphyDownload::GetPersist(nsIWebBrowserPersist * *aPersist) +{ + NS_ENSURE_ARG_POINTER(aPersist); + NS_IF_ADDREF(*aPersist = mWebPersist); + return NS_OK; +} + +/* readonly attribute PRInt32 percentComplete; */ +NS_IMETHODIMP +EphyDownload::GetPercentComplete(PRInt32 *aPercentComplete) +{ + NS_ENSURE_ARG_POINTER(aPercentComplete); + *aPercentComplete = mPercentComplete; + return NS_OK; +} + +/* attribute wstring displayName; */ +NS_IMETHODIMP +EphyDownload::GetDisplayName(PRUnichar * *aDisplayName) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +EphyDownload::SetDisplayName(const PRUnichar * aDisplayName) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* readonly attribute long long startTime; */ +NS_IMETHODIMP +EphyDownload::GetStartTime(PRInt64 *aStartTime) +{ + NS_ENSURE_ARG_POINTER(aStartTime); + *aStartTime = mStartTime; + return NS_OK; +} + +/* readonly attribute nsIMIMEInfo MIMEInfo; */ +NS_IMETHODIMP +EphyDownload::GetMIMEInfo(nsIMIMEInfo * *aMIMEInfo) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* attribute nsIWebProgressListener listener; */ +NS_IMETHODIMP +EphyDownload::GetListener(nsIWebProgressListener * *aListener) +{ + NS_ENSURE_ARG_POINTER(aListener); + NS_IF_ADDREF(*aListener = (nsIWebProgressListener *)this); + return NS_OK; +} + +NS_IMETHODIMP +EphyDownload::SetListener(nsIWebProgressListener * aListener) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* attribute nsIObserver observer; */ +NS_IMETHODIMP +EphyDownload::GetObserver(nsIObserver * *aObserver) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +EphyDownload::SetObserver(nsIObserver * aObserver) +{ + if (aObserver) + aObserver->QueryInterface(NS_GET_IID(nsIHelperAppLauncher), getter_AddRefs(mHelperAppLauncher)); + return NS_OK; +} + +#pragma mark - +#pragma mark [EphyDownload::nsIWebProgressListener] + +/* void onStateChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in unsigned long aStateFlags, in nsresult aStatus); */ +NS_IMETHODIMP +EphyDownload::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; + //BroadcastMessage(msg_OnDLStart, this); + } + + 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))) { + if (mWebPersist) { + mWebPersist->SetProgressListener(nsnull); + mWebPersist = nsnull; + } + mHelperAppLauncher = nsnull; + //BroadcastMessage(msg_OnDLComplete, this); + } + + return NS_OK; +} + +/* void onProgressChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long aCurSelfProgress, in long aMaxSelfProgress, in long aCurTotalProgress, in long aMaxTotalProgress); */ +NS_IMETHODIMP +EphyDownload::OnProgressChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, + PRInt32 aCurSelfProgress, PRInt32 aMaxSelfProgress, + PRInt32 aCurTotalProgress, PRInt32 aMaxTotalProgress) +{ + if (mUserCanceled) { + if (mHelperAppLauncher) + mHelperAppLauncher->Cancel(); + else 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); + + //MsgOnDLProgressChangeInfo info(this, aCurTotalProgress, aMaxTotalProgress); + // From ProgressListener + PRInt64 now = PR_Now(); + mElapsed = now - mStartTime; + + if ((now - mLastUpdate < mInterval) && + (aMaxTotalProgress != -1) && + (aCurTotalProgress < aMaxTotalProgress)) + { + return NS_OK; + } + mLastUpdate = now; + + + gfloat progress = -1; + if (aMaxTotalProgress > 0) + { + progress = (gfloat)aCurTotalProgress / + (gfloat)aMaxTotalProgress; + } + + /* compute download rate */ + gfloat speed = -1; + PRInt64 currentRate; + if (mElapsed) + { + currentRate = ((PRInt64)(aCurTotalProgress)) * 1000000 / mElapsed; + } + else + { + currentRate = 0; + } + + if (!mIsPaused && currentRate) + { + PRFloat64 currentKRate = ((PRFloat64)currentRate)/1024; + if (currentKRate != mPriorKRate) + { + if (mRateChanges++ == mRateChangeLimit) + { + mPriorKRate = currentKRate; + mRateChanges = 0; + } + else + { + currentKRate = mPriorKRate; + } + } + else + { + mRateChanges = 0; + } + + speed = currentKRate; + } + + /* compute time remaining */ + gint remaining = -1; + if (currentRate && (aMaxTotalProgress > 0)) + { + remaining = (gint)((aMaxTotalProgress - aCurTotalProgress) + /currentRate + 0.5); + } + + //BroadcastMessage(msg_OnDLProgressChange, &info); + // UI Rumba + downloader_view_set_download_progress (mDownloaderView, + mElapsed / 1000000, + remaining, + speed, + aMaxTotalProgress / 1024.0 + 0.5, + aCurTotalProgress / 1024.0 + 0.5, + progress, + (gpointer)this); + + return NS_OK; +} + +/* void onLocationChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsIURI location); */ +NS_IMETHODIMP +EphyDownload::OnLocationChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, nsIURI *location) +{ + return NS_OK; +} + +/* void onStatusChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsresult aStatus, in wstring aMessage); */ +NS_IMETHODIMP +EphyDownload::OnStatusChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, + nsresult aStatus, const PRUnichar *aMessage) +{ + return NS_OK; +} + +/* void onSecurityChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in unsigned long state); */ +NS_IMETHODIMP +EphyDownload::OnSecurityChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, PRUint32 state) +{ + return NS_OK; +} + +#pragma mark - +#pragma mark [EphyDownload Internal Methods] + +void +EphyDownload::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 +EphyDownload::Pause() +{ +} + +void +EphyDownload::Resume() +{ +} + +/*void +EphyDownload::CreateProgressView() +{ + sProgressView = new CMultiDownloadProgress; + ThrowIfNil_(sProgressView); +}*/ + + +//***************************************************************************** +// CHelperAppLauncherDialog +//***************************************************************************** +/*#pragma mark - +#pragma mark [CHelperAppLauncherDialog] + +CHelperAppLauncherDialog::CHelperAppLauncherDialog() +{ +} + +CHelperAppLauncherDialog::~CHelperAppLauncherDialog() +{ +} + +NS_IMPL_ISUPPORTS1(CHelperAppLauncherDialog, nsIHelperAppLauncherDialog)*/ + +/* void show (in nsIHelperAppLauncher aLauncher, in nsISupports aContext, in boolean aForced); */ +/*NS_IMETHODIMP CHelperAppLauncherDialog::Show(nsIHelperAppLauncher *aLauncher, nsISupports *aContext, PRBool aForced) +{ + return aLauncher->SaveToDisk(nsnull, PR_FALSE); +}*/ + +/* nsILocalFile promptForSaveToFile (in nsIHelperAppLauncher aLauncher, in nsISupports aWindowContext, in wstring aDefaultFile, in wstring aSuggestedFileExtension); */ +/*NS_IMETHODIMP CHelperAppLauncherDialog::PromptForSaveToFile(nsIHelperAppLauncher* aLauncher, + nsISupports *aWindowContext, + const PRUnichar *aDefaultFile, + const PRUnichar *aSuggestedFileExtension, + nsILocalFile **_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + *_retval = nsnull; + + static bool sFirstTime = true; + UNavServicesDialogs::LFileDesignator designator; + + if (sFirstTime) { + // Get the default download folder and point Nav Sevices to it. + nsCOMPtr<nsIFile> defaultDownloadDir; + NS_GetSpecialDirectory(NS_MAC_DEFAULT_DOWNLOAD_DIR, getter_AddRefs(defaultDownloadDir)); + if (defaultDownloadDir) { + nsCOMPtr<nsILocalFileMac> macDir(do_QueryInterface(defaultDownloadDir)); + FSSpec defaultDownloadSpec; + if (NS_SUCCEEDED(macDir->GetFSSpec(&defaultDownloadSpec))) + designator.SetDefaultLocation(defaultDownloadSpec, true); + } + sFirstTime = false; + } + + Str255 defaultName; + CPlatformUCSConversion::GetInstance()->UCSToPlatform(nsDependentString(aDefaultFile), defaultName); + bool result = designator.AskDesignateFile(defaultName); + + // After the dialog is dismissed, process all activation an update events right away. + // The save dialog code calls UDesktop::Activate after dismissing the dialog. All that + // does is activate the now frontmost LWindow which was behind the dialog. It does not + // remove the activate event from the queue. If that event is not processed and removed + // before we show the progress window, bad things happen. Specifically, the progress + // dialog will show in front and then, shortly thereafter, the window which was behind this save + // dialog will be moved to the front. + + if (LEventDispatcher::GetCurrentEventDispatcher()) { // Can this ever be NULL? + EventRecord theEvent; + while (::WaitNextEvent(updateMask | activMask, &theEvent, 0, nil)) + LEventDispatcher::GetCurrentEventDispatcher()->DispatchEvent(theEvent); + } + + if (result) { + FSSpec destSpec; + designator.GetFileSpec(destSpec); + nsCOMPtr<nsILocalFileMac> destFile; + NS_NewLocalFileWithFSSpec(&destSpec, PR_TRUE, getter_AddRefs(destFile)); + if (!destFile) + return NS_ERROR_FAILURE; + *_retval = destFile; + NS_ADDREF(*_retval); + return NS_OK; + } + else + return NS_ERROR_ABORT; +}*/ + diff --git a/embed/mozilla/EphyDownload.h b/embed/mozilla/EphyDownload.h new file mode 100644 index 000000000..a3abc7067 --- /dev/null +++ b/embed/mozilla/EphyDownload.h @@ -0,0 +1,196 @@ +/* -*- 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> + * + * 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 ***** */ + +#ifndef EphyDownload_h__ +#define EphyDownload_h__ +//#pragma once + +#include "nsIDownload.h" +#include "nsIWebProgressListener.h" +#include "nsIHelperAppLauncherDialog.h" +#include "nsIExternalHelperAppService.h" + +#include "nsIURI.h" +#include "nsILocalFile.h" +#include "nsIWebBrowserPersist.h" + +#include "downloader-view.h" +#include "ephy-embed-shell.h" + +//class ADownloadProgressView; + +//***************************************************************************** +// EphyDownload +// +// Holds information used to display a single download in the UI. This object is +// created in one of two ways: +// (1) By nsExternalHelperAppHandler when Gecko encounters a MIME type which +// it doesn't itself handle. In this case, the notifications sent to +// nsIDownload are controlled by nsExternalHelperAppHandler. +// (2) By the embedding app's file saving code when saving a web page or a link +// target. See CHeaderSniffer.cpp. In this case, the notifications sent to +// nsIDownload are controlled by the implementation of nsIWebBrowserPersist. +//***************************************************************************** + +#define EPHY_DOWNLOAD_CID \ +{ /* d2a2f743-f126-4f1f-1234-d4e50490f112 */ \ + 0xd2a2f743, \ + 0xf126, \ + 0x4f1f, \ + {0x12, 0x34, 0xd4, 0xe5, 0x04, 0x90, 0xf1, 0x12} \ +} + +#define EPHY_DOWNLOAD_CLASSNAME "Ephy's Download Progress Dialog" +//#define EPHY_DOWNLOAD_CONTRACTID "@mozilla.org/progressdialog;1" + +class EphyDownload : public nsIDownload, + public nsIWebProgressListener +// public LBroadcaster +{ +public: + + // Messages we broadcast to listeners. + enum { + msg_OnDLStart = 57723, // param is EphyDownload* + msg_OnDLComplete, // param is EphyDownload* + msg_OnDLProgressChange // param is MsgOnDLProgressChangeInfo* + }; + +/* struct MsgOnDLProgressChangeInfo + { + MsgOnDLProgressChangeInfo(EphyDownload* broadcaster, PRInt32 curProgress, PRInt32 maxProgress) : + mBroadcaster(broadcaster), mCurProgress(curProgress), mMaxProgress(maxProgress) + { } + + EphyDownload *mBroadcaster; + PRInt32 mCurProgress, mMaxProgress; + };*/ + + EphyDownload(); + virtual ~EphyDownload(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIDOWNLOAD + NS_DECL_NSIWEBPROGRESSLISTENER + + virtual void Cancel(); + virtual void Pause(); + virtual void Resume(); + virtual void GetStatus(nsresult& aStatus) + { aStatus = mStatus; } + +//protected: + // void EnsureProgressView() + // { + // if (!sProgressView) + // CreateProgressView(); + // } + // virtual void CreateProgressView(); + // sProgressView is a singleton. This will only be called once. + +protected: + nsCOMPtr<nsIURI> mSource; + nsCOMPtr<nsILocalFile> mDestination; + PRInt64 mStartTime; + PRInt64 mElapsed; + PRInt32 mPercentComplete; + + bool mGotFirstStateChange, mIsNetworkTransfer; + bool mUserCanceled; + bool mIsPaused; + nsresult mStatus; + + // These two are mutually exclusive. + nsCOMPtr<nsIWebBrowserPersist> mWebPersist; + nsCOMPtr<nsIHelperAppLauncher> mHelperAppLauncher; + + PRFloat64 mPriorKRate; + PRInt32 mRateChanges; + PRInt32 mRateChangeLimit; + PRInt64 mLastUpdate; + PRInt32 mInterval; + DownloaderView *mDownloaderView; + // static ADownloadProgressView *sProgressView; +}; + +//***************************************************************************** +// CHelperAppLauncherDialog +// +// The implementation of nsIExternalHelperAppService in Gecko creates one of +// these at the beginning of the download and calls its Show() method. Typically, +// this will create a non-modal dialog in which the user can decide whether to +// save the file to disk or open it with an application. This implementation +// just saves the file to disk unconditionally. The user can decide what they +// wish to do with the download from the progress dialog. +//***************************************************************************** + +/*class CHelperAppLauncherDialog : public nsIHelperAppLauncherDialog +{ +public: + CHelperAppLauncherDialog(); + virtual ~CHelperAppLauncherDialog(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIHELPERAPPLAUNCHERDIALOG + +protected: + +};*/ + + +//***************************************************************************** +// ADownloadProgressView +// +// An abstract class which handles the display and interaction with a download. +// Typically, it presents a progress dialog. +//***************************************************************************** + +/*class ADownloadProgressView +{ + friend class EphyDownload; + + virtual void AddDownloadItem(EphyDownload *aDownloadItem) = 0; + // A download is beginning. Initialize the UI for this download. + // Throughout the download process, the EphyDownload will broadcast + // status messages. The UI needs to call LBroadcaster::AddListener() + // on the EphyDownload at this point in order to get the messages. + +};*/ + + +#endif // EphyDownload_h__ diff --git a/embed/mozilla/EphyHeaderSniffer.cpp b/embed/mozilla/EphyHeaderSniffer.cpp new file mode 100644 index 000000000..ea5df90a0 --- /dev/null +++ b/embed/mozilla/EphyHeaderSniffer.cpp @@ -0,0 +1,457 @@ +/* ***** 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 Chimera 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): + * David Hyatt <hyatt@netscape.com> + * Simon Fraser <sfraser@netscape.com> + * + * 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 ***** */ + +#include "EphyHeaderSniffer.h" +//#include "UMacUnicode.h" + +//#include "UCustomNavServicesDialogs.h" + +#include "netCore.h" + +#include "nsIChannel.h" +#include "nsIHttpChannel.h" +#include "nsIURL.h" +#include "nsIStringEnumerator.h" +#include "nsIPrefService.h" +#include "nsIMIMEService.h" +#include "nsIMIMEInfo.h" +#include "nsIDOMHTMLDocument.h" +#include "nsIDownload.h" +//#include "nsILocalFileMac.h" + +const char* const persistContractID = "@mozilla.org/embedding/browser/nsWebBrowserPersist;1"; + +EphyHeaderSniffer::EphyHeaderSniffer(nsIWebBrowserPersist* aPersist, nsIFile* aFile, nsIURI* aURL, + nsIDOMDocument* aDocument, nsIInputStream* aPostData, + const nsAString& aSuggestedFilename, PRBool aBypassCache, ESaveFormat aSaveFormat) +: mPersist(aPersist) +, mTmpFile(aFile) +, mURL(aURL) +, mDocument(aDocument) +, mPostData(aPostData) +, mDefaultFilename(aSuggestedFilename) +, mBypassCache(aBypassCache) +, mSaveFormat(aSaveFormat) +{ +} + +EphyHeaderSniffer::~EphyHeaderSniffer() +{ +} + +NS_IMPL_ISUPPORTS1(EphyHeaderSniffer, nsIWebProgressListener) + +#pragma mark - + +// Implementation of nsIWebProgressListener +/* void onStateChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long aStateFlags, in unsigned long aStatus); */ +NS_IMETHODIMP +EphyHeaderSniffer::OnStateChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, PRUint32 aStateFlags, + PRUint32 aStatus) +{ + if (aStateFlags & nsIWebProgressListener::STATE_START) + { + nsCOMPtr<nsIWebBrowserPersist> kungFuDeathGrip(mPersist); // be sure to keep it alive while we save + // since it owns us as a listener + nsCOMPtr<nsIWebProgressListener> kungFuSuicideGrip(this); // and keep ourselves alive + + nsresult rv; + nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest, &rv); + if (!channel) return rv; + channel->GetContentType(mContentType); + + nsCOMPtr<nsIURI> origURI; + channel->GetOriginalURI(getter_AddRefs(origURI)); + + // Get the content-disposition if we're an HTTP channel. + nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel)); + if (httpChannel) + httpChannel->GetResponseHeader(nsCAutoString("content-disposition"), mContentDisposition); + + mPersist->CancelSave(); + PRBool exists; + mTmpFile->Exists(&exists); + if (exists) + mTmpFile->Remove(PR_FALSE); + + rv = PerformSave(origURI, mSaveFormat); + if (NS_FAILED(rv)) + { + // put up some UI + + } + } + return NS_OK; +} + +/* void onProgressChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long aCurSelfProgress, in long aMaxSelfProgress, in long aCurTotalProgress, in long aMaxTotalProgress); */ +NS_IMETHODIMP +EphyHeaderSniffer::OnProgressChange(nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + PRInt32 aCurSelfProgress, + PRInt32 aMaxSelfProgress, + PRInt32 aCurTotalProgress, + PRInt32 aMaxTotalProgress) +{ + return NS_OK; +} + +/* void onLocationChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsIURI location); */ +NS_IMETHODIMP +EphyHeaderSniffer::OnLocationChange(nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + nsIURI *location) +{ + return NS_OK; +} + +/* void onStatusChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsresult aStatus, in wstring aMessage); */ +NS_IMETHODIMP +EphyHeaderSniffer::OnStatusChange(nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + nsresult aStatus, + const PRUnichar *aMessage) +{ + return NS_OK; +} + +/* void onSecurityChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in unsigned long state); */ +NS_IMETHODIMP +EphyHeaderSniffer::OnSecurityChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, PRUint32 state) +{ + return NS_OK; +} + +#pragma mark - + +static ESaveFormat SaveFormatFromPrefValue(PRInt32 inPrefValue) +{ + switch (inPrefValue) + { + case 0: return eSaveFormatHTMLComplete; + default: // fall through + case 1: return eSaveFormatHTML; + case 2: return eSaveFormatPlainText; + } +} + +static PRInt32 PrefValueFromSaveFormat(ESaveFormat inSaveFormat) +{ + switch (inSaveFormat) + { + case eSaveFormatPlainText: return 2; + default: // fall through + case eSaveFormatHTML: return 1; + case eSaveFormatHTMLComplete: return 0; + } +} + +nsresult EphyHeaderSniffer::PerformSave(nsIURI* inOriginalURI, const ESaveFormat inSaveFormat) +{ + nsresult rv; + // Are we an HTML document? If so, we will want to append an accessory view to + // the save dialog to provide the user with the option of doing a complete + // save vs. a single file save. + PRBool isHTML = (mDocument && mContentType.Equals("text/html") || + mContentType.Equals("text/xml") || + mContentType.Equals("application/xhtml+xml")); + + // Next find out the directory that we should start in. + nsCOMPtr<nsIPrefService> prefs(do_GetService("@mozilla.org/preferences-service;1", &rv)); + if (!prefs) + return rv; + nsCOMPtr<nsIPrefBranch> dirBranch; + prefs->GetBranch("browser.download.", getter_AddRefs(dirBranch)); + PRInt32 filterIndex = 0; + if (inSaveFormat != eSaveFormatUnspecified) { + filterIndex = PrefValueFromSaveFormat(inSaveFormat); + } + else if (dirBranch) { + nsresult rv = dirBranch->GetIntPref("save_converter_index", &filterIndex); + if (NS_FAILED(rv)) + filterIndex = 0; + } + + // We need to figure out what file name to use. +/* nsAutoString defaultFileName; + if (!mContentDisposition.IsEmpty()) { + // (1) Use the HTTP header suggestion. + PRInt32 index = mContentDisposition.Find("filename="); + if (index >= 0) { + // Take the substring following the prefix. + index += 9; + nsCAutoString filename; + mContentDisposition.Right(filename, mContentDisposition.Length() - index); + defaultFileName = NS_ConvertUTF8toUCS2(filename); + } + } + + if (defaultFileName.IsEmpty()) { + nsCOMPtr<nsIURL> url(do_QueryInterface(mURL)); + if (url) { + nsCAutoString fileNameCString; + url->GetFileName(fileNameCString); // (2) For file URLs, use the file name. + defaultFileName = NS_ConvertUTF8toUCS2(fileNameCString); + } + } + + if (defaultFileName.IsEmpty() && mDocument && isHTML) { + nsCOMPtr<nsIDOMHTMLDocument> htmlDoc(do_QueryInterface(mDocument)); + if (htmlDoc) + htmlDoc->GetTitle(defaultFileName); // (3) Use the title of the document. + } + + if (defaultFileName.IsEmpty()) { + // (4) Use the caller provided name. + defaultFileName = mDefaultFilename; + } + + if (defaultFileName.IsEmpty() && mURL) { + // (5) Use the host. + nsCAutoString hostName; + mURL->GetHost(hostName); + defaultFileName = NS_ConvertUTF8toUCS2(hostName); + } + + // One last case to handle about:blank and other fruity untitled pages. + if (defaultFileName.IsEmpty()) + defaultFileName.AssignWithConversion("untitled"); + + // Validate the file name to ensure legality. + for (PRUint32 i = 0; i < defaultFileName.Length(); i++) + if (defaultFileName[i] == ':' || defaultFileName[i] == '/') + defaultFileName.SetCharAt(i, PRUnichar(' ')); + + // Make sure the appropriate extension is appended to the suggested file name. + nsCOMPtr<nsIURI> fileURI(do_CreateInstance("@mozilla.org/network/standard-url;1")); + nsCOMPtr<nsIURL> fileURL(do_QueryInterface(fileURI, &rv)); + if (!fileURL) + return rv; + + fileURL->SetFilePath(NS_ConvertUCS2toUTF8(defaultFileName)); + + nsCAutoString fileExtension; + fileURL->GetFileExtension(fileExtension); + + PRBool setExtension = PR_FALSE; + if (mContentType.Equals("text/html")) { + if (fileExtension.IsEmpty() || (!fileExtension.Equals("htm") && !fileExtension.Equals("html"))) { + defaultFileName.AppendWithConversion(".html"); + setExtension = PR_TRUE; + } + } + + if (!setExtension && fileExtension.IsEmpty()) { + nsCOMPtr<nsIMIMEService> mimeService(do_GetService("@mozilla.org/mime;1", &rv)); + if (!mimeService) + return rv; + nsCOMPtr<nsIMIMEInfo> mimeInfo; + rv = mimeService->GetFromTypeAndExtension(mContentType.get(), nsnull, getter_AddRefs(mimeInfo)); + if (!mimeInfo) + return rv; + + nsCOMPtr<nsIUTF8StringEnumerator> extensions; + mimeInfo->GetFileExtensions(getter_AddRefs(extensions)); + + PRBool hasMore; + extensions->HasMore(&hasMore); + if (hasMore) { + nsCAutoString ext; + extensions->GetNext(ext); + defaultFileName.Append(PRUnichar('.')); + defaultFileName.Append(NS_ConvertUTF8toUCS2(ext)); + } + } + + // Now it's time to pose the save dialog. + FSSpec destFileSpec; + bool isReplacing = false; + + { + Str255 defaultName; + bool result; + + CPlatformUCSConversion::GetInstance()->UCSToPlatform(defaultFileName, defaultName); +#ifndef XP_MACOSX + char tempBuf1[256], tempBuf2[64]; + ::CopyPascalStringToC(defaultName, tempBuf1); + ::CopyCStringToPascal(NS_TruncNodeName(tempBuf1, tempBuf2), defaultName); +#endif + if (isHTML) { + ESaveFormat saveFormat = SaveFormatFromPrefValue(filterIndex); + UNavServicesDialogs::LCustomFileDesignator customDesignator; + result = customDesignator.AskDesignateFile(defaultName, saveFormat); + if (!result) + return NS_OK; // user canceled + filterIndex = PrefValueFromSaveFormat(saveFormat); + customDesignator.GetFileSpec(destFileSpec); + isReplacing = customDesignator.IsReplacing(); + } + else { + UNavServicesDialogs::LFileDesignator stdDesignator; + result = stdDesignator.AskDesignateFile(defaultName); + if (!result) + return NS_OK; // user canceled + stdDesignator.GetFileSpec(destFileSpec); + isReplacing = stdDesignator.IsReplacing(); + } + + // After the dialog is dismissed, process all activation an update events right away. + // The save dialog code calls UDesktop::Activate after dismissing the dialog. All that + // does is activate the now frontmost LWindow which was behind the dialog. It does not + // remove the activate event from the queue. If that event is not processed and removed + // before we show the progress window, bad things happen. Specifically, the progress + // dialog will show in front and then, shortly thereafter, the window which was behind this save + // dialog will be moved to the front. + + if (LEventDispatcher::GetCurrentEventDispatcher()) { // Can this ever be NULL? + EventRecord theEvent; + while (::WaitNextEvent(updateMask | activMask, &theEvent, 0, nil)) + LEventDispatcher::GetCurrentEventDispatcher()->DispatchEvent(theEvent); + } + } + + // only save the pref if the frontend didn't specify a format + if (inSaveFormat == eSaveFormatUnspecified && isHTML) + dirBranch->SetIntPref("save_converter_index", filterIndex); + + nsCOMPtr<nsILocalFile> destFile; + + rv = NS_NewLocalFileWithFSSpec(&destFileSpec, PR_TRUE, getter_AddRefs(destFile)); + if (NS_FAILED(rv)) + return rv; + + if (isReplacing) { + PRBool exists; + rv = destFile->Exists(&exists); + if (NS_SUCCEEDED(rv) && exists) + rv = destFile->Remove(PR_TRUE); + if (NS_FAILED(rv)) + return rv; + } + + // if the user chose plain text, set the content type + if (isHTML && filterIndex == 2) + mContentType = "text/plain"; + + nsCOMPtr<nsISupports> sourceData; + if (isHTML && filterIndex != 1) + sourceData = do_QueryInterface(mDocument); // HTML complete + else + sourceData = do_QueryInterface(mURL); // HTML or text only + + return InitiateDownload(sourceData, destFile, inOriginalURI);*/ +} + +// inOriginalURI is always a URI. inSourceData can be an nsIURI or an nsIDOMDocument, depending +// on what we're saving. It's that way for nsIWebBrowserPersist. +nsresult EphyHeaderSniffer::InitiateDownload(nsISupports* inSourceData, nsILocalFile* inDestFile, nsIURI* inOriginalURI) +{ + nsresult rv = NS_OK; + + nsCOMPtr<nsIWebBrowserPersist> webPersist = do_CreateInstance(persistContractID, &rv); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr<nsIURI> sourceURI = do_QueryInterface(inSourceData); + + PRInt64 timeNow = PR_Now(); + + nsAutoString fileDisplayName; + inDestFile->GetLeafName(fileDisplayName); + + nsCOMPtr<nsIDownload> downloader = do_CreateInstance(NS_DOWNLOAD_CONTRACTID); + // dlListener attaches to its progress dialog here, which gains ownership + rv = downloader->Init(inOriginalURI, inDestFile, fileDisplayName.get(), nsnull, timeNow, webPersist); + if (NS_FAILED(rv)) return rv; + + PRInt32 flags = nsIWebBrowserPersist::PERSIST_FLAGS_NO_CONVERSION | + nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES; + if (mBypassCache) + flags |= nsIWebBrowserPersist::PERSIST_FLAGS_BYPASS_CACHE; + else + flags |= nsIWebBrowserPersist::PERSIST_FLAGS_FROM_CACHE; + + webPersist->SetPersistFlags(flags); + + if (sourceURI) + { + rv = webPersist->SaveURI(sourceURI, nsnull, nsnull, mPostData, nsnull, inDestFile); + } + else + { + nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(inSourceData, &rv); + if (!domDoc) return rv; // should never happen + + PRInt32 encodingFlags = 0; + nsCOMPtr<nsILocalFile> filesFolder; + + if (!mContentType.Equals("text/plain")) { + // 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"); // XXXdwh needs to be localizable! + 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(domDoc, inDestFile, filesFolder, mContentType.get(), encodingFlags, 80); + } + + return rv; +} diff --git a/embed/mozilla/EphyHeaderSniffer.h b/embed/mozilla/EphyHeaderSniffer.h new file mode 100644 index 000000000..8ef6afa01 --- /dev/null +++ b/embed/mozilla/EphyHeaderSniffer.h @@ -0,0 +1,89 @@ +/* ***** 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 Chimera 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): + * David Hyatt <hyatt@netscape.com> + * Simon Fraser <sfraser@netscape.com> + * + * 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 ***** */ + + #pragma once + +#include "nsString.h" +#include "nsIWebProgressListener.h" +#include "nsIWebBrowserPersist.h" +#include "nsIURI.h" +#include "nsILocalFile.h" +#include "nsIInputStream.h" +#include "nsIDOMDocument.h" + + +typedef enum +{ + eSaveFormatUnspecified = 0, + eSaveFormatPlainText, // items should match the MENU in resources + eSaveFormatHTML, + eSaveFormatHTMLComplete +} ESaveFormat; + +// Implementation of a header sniffer class that is used when saving Web pages and images. +class EphyHeaderSniffer : public nsIWebProgressListener +{ +public: + EphyHeaderSniffer(nsIWebBrowserPersist* aPersist, nsIFile* aFile, nsIURI* aURL, + nsIDOMDocument* aDocument, nsIInputStream* aPostData, + const nsAString& aSuggestedFilename, PRBool aBypassCache, + ESaveFormat aSaveFormat = eSaveFormatUnspecified); + virtual ~EphyHeaderSniffer(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIWEBPROGRESSLISTENER + +protected: + + nsresult PerformSave(nsIURI* inOriginalURI, const ESaveFormat inSaveFormat); + nsresult InitiateDownload(nsISupports* inSourceData, nsILocalFile* inDestFile, nsIURI* inOriginalURI); + +private: + + nsIWebBrowserPersist* mPersist; // Weak. It owns us as a listener. + nsCOMPtr<nsIFile> mTmpFile; + nsCOMPtr<nsIURI> mURL; + nsCOMPtr<nsIDOMDocument> mDocument; + nsCOMPtr<nsIInputStream> mPostData; + nsString mDefaultFilename; + PRBool mBypassCache; + ESaveFormat mSaveFormat; + nsCString mContentType; + nsCString mContentDisposition; +}; + |