Adding new files for bug 162845 - PPEmbed needs native implementation of download UI. r=pink/sr=sfraser

This commit is contained in:
ccarlen%netscape.com 2002-09-20 22:56:13 +00:00
Родитель af668a0285
Коммит 9f118d72bb
10 изменённых файлов: 1723 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,79 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** 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):
* Brian Ryner <bryner@netscape.com>
* 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 "AppComponents.h"
#include "PromptService.h"
#include "UDownload.h"
#define NS_PROMPTSERVICE_CID \
{0xa2112d6a, 0x0e28, 0x421f, {0xb4, 0x6a, 0x25, 0xc0, 0xb3, 0x8, 0xcb, 0xd0}}
#define NS_HELPERAPPLAUNCHERDIALOG_CID \
{0xf68578eb, 0x6ec2, 0x4169, {0xae, 0x19, 0x8c, 0x62, 0x43, 0xf0, 0xab, 0xe1}}
NS_GENERIC_FACTORY_CONSTRUCTOR(CPromptService);
NS_GENERIC_FACTORY_CONSTRUCTOR(CDownload);
NS_GENERIC_FACTORY_CONSTRUCTOR(CHelperAppLauncherDialog);
static const nsModuleComponentInfo components[] = {
{
"Prompt Service",
NS_PROMPTSERVICE_CID,
"@mozilla.org/embedcomp/prompt-service;1",
CPromptServiceConstructor
},
{
"Download",
NS_DOWNLOAD_CID,
NS_DOWNLOAD_CONTRACTID,
CDownloadConstructor
},
{
NS_IHELPERAPPLAUNCHERDLG_CLASSNAME,
NS_HELPERAPPLAUNCHERDIALOG_CID,
NS_IHELPERAPPLAUNCHERDLG_CONTRACTID,
CHelperAppLauncherDialogConstructor
}
};
const nsModuleComponentInfo* GetAppModuleComponentInfo(int* outNumComponents)
{
*outNumComponents = sizeof(components) / sizeof(components[0]);
return components;
}

Просмотреть файл

@ -0,0 +1,51 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** 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):
* Brian Ryner <bryner@netscape.com>
* 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 AppComponents_h__
#define AppComponents_h__
#ifndef nsIGenericFactory_h___
#include "nsIGenericFactory.h"
#endif
// This method should return a nsModuleComponentInfo array of
// application-provided XPCOM components to register.
extern const nsModuleComponentInfo* GetAppModuleComponentInfo(int* outNumComponents);
#endif

Просмотреть файл

@ -0,0 +1,452 @@
/* ***** 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 "CHeaderSniffer.h"
#include "UMacUnicode.h"
#include "UCustomNavServicesDialogs.h"
#include "netCore.h"
#include "nsIChannel.h"
#include "nsIHttpChannel.h"
#include "nsIURL.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";
CHeaderSniffer::CHeaderSniffer(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)
{
NS_INIT_ISUPPORTS();
}
CHeaderSniffer::~CHeaderSniffer()
{
}
NS_IMPL_ISUPPORTS1(CHeaderSniffer, nsIWebProgressListener)
#pragma mark -
// Implementation of nsIWebProgressListener
/* void onStateChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long aStateFlags, in unsigned long aStatus); */
NS_IMETHODIMP
CHeaderSniffer::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
CHeaderSniffer::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
CHeaderSniffer::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
CHeaderSniffer::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
CHeaderSniffer::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 CHeaderSniffer::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->GetFromMIMEType(mContentType.get(), getter_AddRefs(mimeInfo));
if (!mimeInfo)
return rv;
PRUint32 extCount = 0;
char** extList = nsnull;
mimeInfo->GetFileExtensions(&extCount, &extList);
if (extCount > 0 && extList) {
defaultFileName.AppendWithConversion(".");
defaultFileName.AppendWithConversion(extList[0]);
}
}
// Now it's time to pose the save dialog.
FSSpec destFileSpec;
bool isReplacing = false;
{
Str255 defaultName;
char tempBuf1[256], tempBuf2[64];
bool result;
CPlatformUCSConversion::GetInstance()->UCSToPlatform(defaultFileName, defaultName);
::CopyPascalStringToC(defaultName, tempBuf1);
::CopyCStringToPascal(NS_TruncNodeName(tempBuf1, tempBuf2), defaultName);
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<nsILocalFileMac> 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 CHeaderSniffer::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, mPostData, 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;
}

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -0,0 +1,355 @@
/* -*- 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 "UDownload.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"
//*****************************************************************************
// CDownload
//*****************************************************************************
#pragma mark [CDownload]
ADownloadProgressView *CDownload::sProgressView;
CDownload::CDownload() :
mGotFirstStateChange(false), mIsNetworkTransfer(false),
mUserCanceled(false),
mStatus(NS_OK)
{
NS_INIT_ISUPPORTS();
}
CDownload::~CDownload()
{
}
NS_IMPL_ISUPPORTS2(CDownload, nsIDownload, nsIWebProgressListener)
#pragma mark -
#pragma mark [CDownload::nsIDownload]
/* void init (in nsIURI aSource, in nsILocalFile aTarget, in wstring aDisplayName, in nsIMIMEInfo aMIMEInfo, in long long startTime, in nsIWebBrowserPersist aPersist); */
NS_IMETHODIMP CDownload::Init(nsIURI *aSource, nsILocalFile *aTarget, const PRUnichar *aDisplayName, nsIMIMEInfo *aMIMEInfo, PRInt64 startTime, nsIWebBrowserPersist *aPersist)
{
try {
mSource = aSource;
mDestination = aTarget;
mStartTime = startTime;
mPercentComplete = 0;
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);
}
EnsureProgressView();
sProgressView->AddDownloadItem(this);
}
catch (...) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
/* readonly attribute nsIURI source; */
NS_IMETHODIMP CDownload::GetSource(nsIURI * *aSource)
{
NS_ENSURE_ARG_POINTER(aSource);
NS_IF_ADDREF(*aSource = mSource);
return NS_OK;
}
/* readonly attribute nsILocalFile target; */
NS_IMETHODIMP CDownload::GetTarget(nsILocalFile * *aTarget)
{
NS_ENSURE_ARG_POINTER(aTarget);
NS_IF_ADDREF(*aTarget = mDestination);
return NS_OK;
}
/* readonly attribute nsIWebBrowserPersist persist; */
NS_IMETHODIMP CDownload::GetPersist(nsIWebBrowserPersist * *aPersist)
{
NS_ENSURE_ARG_POINTER(aPersist);
NS_IF_ADDREF(*aPersist = mWebPersist);
return NS_OK;
}
/* readonly attribute PRInt32 percentComplete; */
NS_IMETHODIMP CDownload::GetPercentComplete(PRInt32 *aPercentComplete)
{
NS_ENSURE_ARG_POINTER(aPercentComplete);
*aPercentComplete = mPercentComplete;
return NS_OK;
}
/* attribute wstring displayName; */
NS_IMETHODIMP CDownload::GetDisplayName(PRUnichar * *aDisplayName)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP CDownload::SetDisplayName(const PRUnichar * aDisplayName)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* readonly attribute long long startTime; */
NS_IMETHODIMP CDownload::GetStartTime(PRInt64 *aStartTime)
{
NS_ENSURE_ARG_POINTER(aStartTime);
*aStartTime = mStartTime;
return NS_OK;
}
/* readonly attribute nsIMIMEInfo MIMEInfo; */
NS_IMETHODIMP CDownload::GetMIMEInfo(nsIMIMEInfo * *aMIMEInfo)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* attribute nsIWebProgressListener listener; */
NS_IMETHODIMP CDownload::GetListener(nsIWebProgressListener * *aListener)
{
NS_ENSURE_ARG_POINTER(aListener);
NS_IF_ADDREF(*aListener = (nsIWebProgressListener *)this);
return NS_OK;
}
NS_IMETHODIMP CDownload::SetListener(nsIWebProgressListener * aListener)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* attribute nsIObserver observer; */
NS_IMETHODIMP CDownload::GetObserver(nsIObserver * *aObserver)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP CDownload::SetObserver(nsIObserver * aObserver)
{
if (aObserver)
aObserver->QueryInterface(NS_GET_IID(nsIHelperAppLauncher), getter_AddRefs(mHelperAppLauncher));
return NS_OK;
}
#pragma mark -
#pragma mark [CDownload::nsIWebProgressListener]
/* void onStateChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in unsigned long aStateFlags, in nsresult aStatus); */
NS_IMETHODIMP CDownload::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 CDownload::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 = ((float)aCurTotalProgress / (float)aMaxTotalProgress) * 100.0;
MsgOnDLProgressChangeInfo info(this, aCurTotalProgress, aMaxTotalProgress);
BroadcastMessage(msg_OnDLProgressChange, &info);
return NS_OK;
}
/* void onLocationChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsIURI location); */
NS_IMETHODIMP CDownload::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 CDownload::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 CDownload::OnSecurityChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, PRUint32 state)
{
return NS_OK;
}
#pragma mark -
#pragma mark [CDownload Internal Methods]
void CDownload::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 CDownload::CreateProgressView()
{
sProgressView = new CMultiDownloadProgress;
ThrowIfNil_(sProgressView);
}
//*****************************************************************************
// CHelperAppLauncherDialog
//*****************************************************************************
#pragma mark -
#pragma mark [CHelperAppLauncherDialog]
CHelperAppLauncherDialog::CHelperAppLauncherDialog()
{
NS_INIT_ISUPPORTS();
}
CHelperAppLauncherDialog::~CHelperAppLauncherDialog()
{
}
NS_IMPL_ISUPPORTS1(CHelperAppLauncherDialog, nsIHelperAppLauncherDialog)
/* void show (in nsIHelperAppLauncher aLauncher, in nsISupports aContext); */
NS_IMETHODIMP CHelperAppLauncherDialog::Show(nsIHelperAppLauncher *aLauncher, nsISupports *aContext)
{
return aLauncher->SaveToDisk(nsnull, PR_FALSE);
}
/* nsILocalFile promptForSaveToFile (in nsISupports aWindowContext, in wstring aDefaultFile, in wstring aSuggestedFileExtension); */
NS_IMETHODIMP CHelperAppLauncherDialog::PromptForSaveToFile(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;
}
/* void showProgressDialog (in nsIHelperAppLauncher aLauncher, in nsISupports aContext); */
NS_IMETHODIMP CHelperAppLauncherDialog::ShowProgressDialog(nsIHelperAppLauncher *aLauncher, nsISupports *aContext)
{
return NS_ERROR_NOT_IMPLEMENTED;
}

Просмотреть файл

@ -0,0 +1,171 @@
/* -*- 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 UDownload_h__
#define UDownload_h__
#pragma once
#include "nsIDownload.h"
#include "nsIWebProgressListener.h"
#include "nsIHelperAppLauncherDialog.h"
#include "nsIURI.h"
#include "nsILocalFile.h"
#include "nsIWebBrowserPersist.h"
class ADownloadProgressView;
//*****************************************************************************
// CDownload
//
// 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.
//*****************************************************************************
class CDownload : public nsIDownload,
public nsIWebProgressListener,
public LBroadcaster
{
public:
// Messages we broadcast to listeners.
enum {
msg_OnDLStart = 57723, // param is CDownload*
msg_OnDLComplete, // param is CDownload*
msg_OnDLProgressChange // param is MsgOnDLProgressChangeInfo*
};
struct MsgOnDLProgressChangeInfo
{
MsgOnDLProgressChangeInfo(CDownload* broadcaster, PRInt32 curProgress, PRInt32 maxProgress) :
mBroadcaster(broadcaster), mCurProgress(curProgress), mMaxProgress(maxProgress)
{ }
CDownload *mBroadcaster;
PRInt32 mCurProgress, mMaxProgress;
};
CDownload();
virtual ~CDownload();
NS_DECL_ISUPPORTS
NS_DECL_NSIDOWNLOAD
NS_DECL_NSIWEBPROGRESSLISTENER
virtual void Cancel();
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;
PRInt32 mPercentComplete;
bool mGotFirstStateChange, mIsNetworkTransfer;
bool mUserCanceled;
nsresult mStatus;
// These two are mutually exclusive.
nsCOMPtr<nsIWebBrowserPersist> mWebPersist;
nsCOMPtr<nsIHelperAppLauncher> mHelperAppLauncher;
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 CDownload;
virtual void AddDownloadItem(CDownload *aDownloadItem) = 0;
// A download is beginning. Initialize the UI for this download.
// Throughout the download process, the CDownload will broadcast
// status messages. The UI needs to call LBroadcaster::AddListener()
// on the CDownload at this point in order to get the messages.
};
#endif // UDownload_h__

Просмотреть файл

@ -0,0 +1,615 @@
/* -*- 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 "UDownloadDisplay.h"
#include "ApplIDs.h"
#include "CIconServicesIcon.h"
// Gecko
#include "nsString.h"
#include "nsILocalFileMac.h"
// Std
#include <algorithm>
using namespace std;
// PowerPlant
#include <LStaticText.h>
#include <LProgressBar.h>
//*****************************************************************************
// CMultiDownloadProgress
//*****************************************************************************
#pragma mark [CMultiDownloadProgress]
bool CMultiDownloadProgress::sRegisteredViewClasses = false;
CMultiDownloadProgress::CMultiDownloadProgress() :
mWindow(nil)
{
if (!sRegisteredViewClasses) {
RegisterClass_(CMultiDownloadProgressWindow);
RegisterClass_(CDownloadProgressView);
RegisterClass_(CIconServicesIcon);
sRegisteredViewClasses = true;
}
}
CMultiDownloadProgress::~CMultiDownloadProgress()
{
}
void CMultiDownloadProgress::AddDownloadItem(CDownload *aDownloadItem)
{
if (!mWindow) {
mWindow = static_cast<CMultiDownloadProgressWindow*>
(LWindow::CreateWindow(wind_DownloadProgress, this));
ThrowIfNil_(mWindow);
// Add this as a listener to the window so we can know when it's destroyed.
mWindow->AddListener(this);
}
// Create the view...
LView::SetDefaultView(mWindow);
LCommander::SetDefaultCommander(mWindow);
LAttachable::SetDefaultAttachable(nil);
CDownloadProgressView *itemView = static_cast<CDownloadProgressView*>
(UReanimator::ReadObjects(ResType_PPob, view_DownloadProgressItem));
ThrowIfNil_(itemView);
// and add it to the window.
mWindow->AddDownloadView(itemView);
itemView->SetDownload(aDownloadItem);
}
// This happens in response to the window being closed. Check for active
// downloads and confirm stopping them if there are any. Returning false
// will prevent the window from being closed.
Boolean CMultiDownloadProgress::AllowSubRemoval(LCommander* inSub)
{
return (!mWindow || (inSub == mWindow && mWindow->ConfirmClose()));
}
// This happens in response to the app being quit. Check for active
// downloads and confirm stopping them if there are any. Returning
// false will prevent the app from quitting.
Boolean CMultiDownloadProgress::AttemptQuitSelf(SInt32 inSaveOption)
{
return (!mWindow || mWindow->ConfirmClose());
}
void CMultiDownloadProgress::ListenToMessage(MessageT inMessage,
void* ioParam)
{
if (inMessage == msg_BroadcasterDied &&
(CMultiDownloadProgressWindow*)((LBroadcaster*)ioParam) == mWindow)
mWindow = nil;
}
//*****************************************************************************
// CMultiDownloadProgressWindow
//*****************************************************************************
#pragma mark -
#pragma mark [CMultiDownloadProgressWindow]
CMultiDownloadProgressWindow::CMultiDownloadProgressWindow() :
mDownloadViewCount(0)
{
StartBroadcasting();
}
CMultiDownloadProgressWindow::CMultiDownloadProgressWindow(LStream* inStream) :
LWindow(inStream),
mDownloadViewCount(0)
{
StartBroadcasting();
}
CMultiDownloadProgressWindow::~CMultiDownloadProgressWindow()
{
}
void CMultiDownloadProgressWindow::AddDownloadView(CDownloadProgressView *aView)
{
const SInt16 kSeparatorHeight = 3;
SDimension16 currSize;
GetFrameSize(currSize);
SDimension16 viewSize;
aView->GetFrameSize(viewSize);
ResizeWindowTo(currSize.width, (viewSize.height * (mDownloadViewCount + 1)) - kSeparatorHeight);
aView->PlaceInSuperFrameAt(0, viewSize.height * mDownloadViewCount++, false);
aView->FinishCreate();
}
void CMultiDownloadProgressWindow::RemoveDownloadView(CDownloadProgressView *aView)
{
// We can't remove the last view, leaving an empty window frame
if (mDownloadViewCount <= 1)
return;
SDimension16 removedPaneSize;
aView->GetFrameSize(removedPaneSize);
SPoint32 removedPaneLoc;
aView->GetFrameLocation(removedPaneLoc);
delete aView;
RemoveSubPane(aView);
mDownloadViewCount--;
TArrayIterator<LPane*> iterator(GetSubPanes());
LPane *subPane;
while (iterator.Next(subPane)) {
SPoint32 subPaneLoc;
subPane->GetFrameLocation(subPaneLoc);
if (subPaneLoc.v >= removedPaneLoc.v + removedPaneSize.height) {
subPane->MoveBy(0, -removedPaneSize.height, true);
}
}
ResizeWindowBy(0, -removedPaneSize.height);
}
Boolean CMultiDownloadProgressWindow::ConfirmClose()
{
Boolean canClose = true;
SInt32 numActiveDownloads = 0;
TArrayIterator<LPane*> iterator(GetSubPanes());
LPane *subPane;
while (iterator.Next(subPane)) {
CDownloadProgressView *downloadView = dynamic_cast<CDownloadProgressView*>(subPane);
if (downloadView && downloadView->IsActive())
numActiveDownloads++;
}
if (numActiveDownloads != 0) {
short itemHit;
AlertStdAlertParamRec pb;
pb.movable = false;
pb.helpButton = false;
pb.filterProc = nil;
pb.defaultText = (StringPtr) kAlertDefaultOKText;
pb.cancelText = (StringPtr) kAlertDefaultCancelText;
pb.otherText = nil;
pb.defaultButton = kStdOkItemIndex;
pb.cancelButton = kStdCancelItemIndex;
pb.position = kWindowAlertPositionParentWindowScreen;
LStr255 msgString(STRx_StdAlertStrings, str_ConfirmCloseDownloads);
LStr255 explainString(STRx_StdAlertStrings, str_ConfirmCloseDownloadsExp);
::StandardAlert(kAlertStopAlert, msgString, explainString, &pb, &itemHit);
if (itemHit != kAlertStdAlertOKButton)
canClose = false;
}
return canClose;
}
//*****************************************************************************
// CDownloadProgressView
//*****************************************************************************
#pragma mark -
#pragma mark [CDownloadProgressView]
enum {
paneID_ProgressBar = 'Prog',
paneID_CancelButton = 'Cncl',
paneID_OpenButton = 'Open',
paneID_RevealButton = 'Rvel',
paneID_CloseButton = 'Clos',
paneID_StatusText = 'Stat',
paneID_StatusLabel = 'StLa',
paneID_TimeRemText = 'Time',
paneID_TimeRemLabel = 'TiLa',
paneID_SrcURIText = 'SURI',
paneID_SrcURILabel = 'SULa',
paneID_DestFileText = 'Dest',
paneID_DestFileLabel = 'DFLa'
};
CDownloadProgressView::CDownloadProgressView() :
mDownloadActive(false)
{
}
CDownloadProgressView::CDownloadProgressView(LStream* inStream) :
LView(inStream),
mDownloadActive(false)
{
}
CDownloadProgressView::~CDownloadProgressView()
{
if (mDownloadActive)
CancelDownload();
mDownload = nsnull;
}
void CDownloadProgressView::FinishCreateSelf()
{
mProgressBar = dynamic_cast<LProgressBar*>(FindPaneByID(paneID_ProgressBar));
mCancelButton = dynamic_cast<LControl*>(FindPaneByID(paneID_CancelButton));
mOpenButton = dynamic_cast<LControl*>(FindPaneByID(paneID_OpenButton));
mRevealButton = dynamic_cast<LControl*>(FindPaneByID(paneID_RevealButton));
mCloseButton = dynamic_cast<LControl*>(FindPaneByID(paneID_CloseButton));
mStatusText = dynamic_cast<LStaticText*>(FindPaneByID(paneID_StatusText));
mTimeRemainingText = dynamic_cast<LStaticText*>(FindPaneByID(paneID_TimeRemText));
mSrcURIText = dynamic_cast<LStaticText*>(FindPaneByID(paneID_SrcURIText));
mDestFileText = dynamic_cast<LStaticText*>(FindPaneByID(paneID_DestFileText));
// The control value is set in terms of percent
if (mProgressBar)
mProgressBar->SetMaxValue(100);
ControlFontStyleRec styleRec;
if (CreateStyleRecFromThemeFont(kThemeSmallSystemFont, styleRec) == noErr) {
if (mStatusText)
mStatusText->SetFontStyle(styleRec);
if (mTimeRemainingText)
mTimeRemainingText->SetFontStyle(styleRec);
if (mSrcURIText)
mSrcURIText->SetFontStyle(styleRec);
if (mDestFileText)
mDestFileText->SetFontStyle(styleRec);
}
if (CreateStyleRecFromThemeFont(kThemeSmallEmphasizedSystemFont, styleRec) == noErr) {
ResIDT labelIDs [] = { paneID_StatusLabel,
paneID_TimeRemLabel,
paneID_SrcURILabel,
paneID_DestFileLabel };
for (int i = 0; i < sizeof(labelIDs) / sizeof(labelIDs[0]); i++) {
LStaticText *staticText = dynamic_cast<LStaticText*>(FindPaneByID(labelIDs[i]));
if (staticText)
staticText->SetFontStyle(styleRec);
}
}
UReanimator::LinkListenerToControls(this, this, view_DownloadProgressItem);
StartListening();
}
Boolean CDownloadProgressView::ObeyCommand(CommandT inCommand,
void *ioParam)
{
#pragma unused(ioParam)
Boolean cmdHandled = false;
nsCOMPtr<nsILocalFile> targetFile;
switch (inCommand)
{
case paneID_CancelButton:
{
CancelDownload();
cmdHandled = true;
}
break;
case paneID_OpenButton:
{
mDownload->GetTarget(getter_AddRefs(targetFile));
if (targetFile)
targetFile->Launch();
}
break;
case paneID_RevealButton:
{
mDownload->GetTarget(getter_AddRefs(targetFile));
if (targetFile)
targetFile->Reveal();
}
break;
case paneID_CloseButton:
{
LView *view = this, *superView;
while ((superView = view->GetSuperView()) != nil)
view = superView;
CMultiDownloadProgressWindow *multiWindow = dynamic_cast<CMultiDownloadProgressWindow*>(view);
if (multiWindow)
multiWindow->RemoveDownloadView(this);
}
break;
}
return cmdHandled;
}
void CDownloadProgressView::ListenToMessage(MessageT inMessage,
void* ioParam)
{
switch (inMessage) {
case CDownload::msg_OnDLStart:
{
mDownloadActive = true;
if (mCancelButton)
mCancelButton->Enable();
}
break;
case CDownload::msg_OnDLComplete:
{
mDownloadActive = false;
if (mCancelButton)
mCancelButton->Disable();
if (mCloseButton)
mCloseButton->Enable();
CDownload *download = reinterpret_cast<CDownload*>(ioParam);
nsresult downloadStatus;
download->GetStatus(downloadStatus);
// When saving documents as plain text, we might not get any
// progress change notifications in which to set the progress bar.
// Now that the download is done, put the bar in a reasonable state.
if (mProgressBar) {
mProgressBar->SetIndeterminateFlag(false, false);
if (NS_SUCCEEDED(downloadStatus))
mProgressBar->SetValue(mProgressBar->GetMaxValue());
else
mProgressBar->SetValue(0);
}
if (NS_SUCCEEDED(downloadStatus)) {
if (mOpenButton)
mOpenButton->Enable();
}
if (mRevealButton)
mRevealButton->Enable();
if (mTimeRemainingText)
mTimeRemainingText->SetText(LStr255("\p"));
}
break;
case CDownload::msg_OnDLProgressChange:
{
CDownload::MsgOnDLProgressChangeInfo *info =
static_cast<CDownload::MsgOnDLProgressChangeInfo*>(ioParam);
if (mProgressBar) {
PRInt32 percentComplete;
info->mBroadcaster->GetPercentComplete(&percentComplete);
if (percentComplete != -1 && mProgressBar->IsIndeterminate())
mProgressBar->SetIndeterminateFlag(false, false);
else if (percentComplete == -1 && !mProgressBar->IsIndeterminate())
mProgressBar->SetIndeterminateFlag(true, true);
if (!mProgressBar->IsIndeterminate()) {
PRInt32 controlVal = min(100, max(0, percentComplete));
mProgressBar->SetValue(controlVal);
}
}
// Set the progress bar as often as we're called. Smooth movement is nice.
// Limit the frequency at which the textual status is updated though.
if (info->mCurProgress == info->mMaxProgress ||
::TickCount() - mLastStatusUpdateTicks >= kStatusUpdateIntervalTicks) {
UpdateStatus(info);
mLastStatusUpdateTicks = ::TickCount();
}
}
break;
default:
ProcessCommand(inMessage, ioParam);
break;
}
}
void CDownloadProgressView::SetDownload(CDownload *aDownload)
{
mDownload = aDownload;
if (!mDownload)
return;
mDownloadActive = true;
mLastStatusUpdateTicks = ::TickCount();
aDownload->AddListener(this);
nsresult rv;
nsCAutoString tempStr;
if (mSrcURIText) {
nsCOMPtr<nsIURI> srcURI;
aDownload->GetSource(getter_AddRefs(srcURI));
if (srcURI) {
rv = srcURI->GetSpec(tempStr);
if (NS_SUCCEEDED(rv))
mSrcURIText->SetText(const_cast<Ptr>(PromiseFlatCString(tempStr).get()), tempStr.Length());
}
}
if (mDestFileText) {
nsCOMPtr<nsILocalFile> destFile;
aDownload->GetTarget(getter_AddRefs(destFile));
if (destFile) {
rv = destFile->GetNativePath(tempStr);
if (NS_SUCCEEDED(rv))
mDestFileText->SetText(const_cast<Ptr>(PromiseFlatCString(tempStr).get()), tempStr.Length());
}
}
// At this point, make sure our window is showing.
LWindow::FetchWindowObject(GetMacWindow())->Show();
}
void CDownloadProgressView::CancelDownload()
{
CDownload *download = dynamic_cast<CDownload*>(mDownload.get());
download->Cancel();
}
Boolean CDownloadProgressView::IsActive()
{
return mDownloadActive;
}
void CDownloadProgressView::UpdateStatus(CDownload::MsgOnDLProgressChangeInfo *info)
{
PRInt64 startTime;
mDownload->GetStartTime(&startTime);
PRInt32 elapsedSecs = (PR_Now() - startTime) / PR_USEC_PER_SEC;
float bytesPerSec = info->mCurProgress / elapsedSecs;
UInt8 startPos;
LStr255 valueStr;
if (mStatusText) {
// "@1 of @2 (at @3/sec)"
LStr255 formatStr(STRx_DownloadStatus, str_ProgressFormat);
// Insert each item into the string individually in order to
// allow certain elements to be omitted from the format.
if ((startPos = formatStr.Find("\p@1")) != 0)
formatStr.Replace(startPos, 2, FormatBytes(info->mCurProgress, valueStr));
if ((startPos = formatStr.Find("\p@2")) != 0)
formatStr.Replace(startPos, 2, FormatBytes(info->mMaxProgress, valueStr));
if ((startPos = formatStr.Find("\p@3")) != 0)
formatStr.Replace(startPos, 2, FormatBytes(bytesPerSec, valueStr));
mStatusText->SetText(formatStr);
}
if (mTimeRemainingText) {
PRInt32 secsRemaining = rint(float(info->mMaxProgress - info->mCurProgress) / bytesPerSec);
mTimeRemainingText->SetText(FormatFuzzyTime(secsRemaining, valueStr));
}
}
LStr255& CDownloadProgressView::FormatBytes(float inBytes, LStr255& ioString)
{
const float kOneThousand24 = 1024.0;
char buf[256];
if (inBytes < 0) {
return (ioString = "???");
}
if (inBytes < kOneThousand24) {
sprintf(buf, "%.1f Bytes", inBytes);
return (ioString = buf);
}
inBytes /= kOneThousand24;
if (inBytes < 1024) {
sprintf(buf, "%.1f KB", inBytes);
return (ioString = buf);
}
inBytes /= kOneThousand24;
if (inBytes < 1024) {
sprintf(buf, "%.1f MB", inBytes);
return (ioString = buf);
}
inBytes /= kOneThousand24;
sprintf(buf, "%.2f GB", inBytes);
return (ioString = buf);
}
LStr255& CDownloadProgressView::FormatFuzzyTime(PRInt32 inSecs, LStr255& ioString)
{
char valueBuf[32];
if (inSecs < 90) {
if (inSecs < 7)
ioString.Assign(STRx_DownloadStatus, str_About5Seconds);
else if (inSecs < 13)
ioString.Assign(STRx_DownloadStatus, str_About10Seconds);
else if (inSecs < 60)
ioString.Assign(STRx_DownloadStatus, str_LessThan1Minute);
else
ioString.Assign(STRx_DownloadStatus, str_About1Minute);
return ioString;
}
inSecs = (inSecs + 30) / 60; // Round up so we don't say "About 1 minutes"
if (inSecs < 60) {
sprintf(valueBuf, "%d", inSecs);
ioString.Assign(STRx_DownloadStatus, str_AboutNMinutes);
ioString.Replace(ioString.Find("\p@1"), 2, LStr255(valueBuf));
return ioString;
}
inSecs /= 60;
if (inSecs == 1)
ioString.Assign(STRx_DownloadStatus, str_About1Hour);
else {
sprintf(valueBuf, "%d", inSecs);
ioString.Assign(STRx_DownloadStatus, str_AboutNHours);
ioString.Replace(ioString.Find("\p@1"), 2, LStr255(valueBuf));
}
return ioString;
}
OSErr CDownloadProgressView::CreateStyleRecFromThemeFont(ThemeFontID inThemeID,
ControlFontStyleRec& outStyle)
{
Str255 themeFontName;
SInt16 themeFontSize;
Style themeStyle;
SInt16 themeFontNum;
OSErr err = ::GetThemeFont(kThemeSmallSystemFont,
smSystemScript,
themeFontName,
&themeFontSize,
&themeStyle);
if (err != noErr)
return err;
outStyle.flags = kControlUseFontMask +
kControlUseFaceMask +
kControlUseSizeMask;
::GetFNum(themeFontName, &themeFontNum);
outStyle.font = themeFontNum;
outStyle.size = themeFontSize;
outStyle.style = themeStyle;
return noErr;
}

Просмотреть файл