diff --git a/modules/progress/public/MANIFEST b/modules/progress/public/MANIFEST index 4f85ec550816..7ac6b518b04a 100644 --- a/modules/progress/public/MANIFEST +++ b/modules/progress/public/MANIFEST @@ -3,3 +3,6 @@ # pw_public.h +nsITransferListener.h +nsProgressManager.h +progress.h diff --git a/modules/progress/public/Makefile b/modules/progress/public/Makefile index 1fc6e395d637..1fc3c73df1b7 100644 --- a/modules/progress/public/Makefile +++ b/modules/progress/public/Makefile @@ -21,4 +21,12 @@ MODULE = progress EXPORTS = pw_public.h +ifdef MOZ_SMOOTH_PROGRESS +EXPORTS += \ + nsITransferListener.h \ + nsProgressManager.h \ + progress.h \ + $(NULL) +endif + include $(DEPTH)/config/rules.mk diff --git a/modules/progress/public/makefile.win b/modules/progress/public/makefile.win index 04a6feb6dfee..31311642cb9e 100644 --- a/modules/progress/public/makefile.win +++ b/modules/progress/public/makefile.win @@ -20,7 +20,15 @@ IGNORE_MANIFEST=1 MODULE=progress DEPTH=..\..\.. -EXPORTS=pw_public.h + +EXPORTS=\ +!ifdef MOZ_SMOOTH_PROGRESS + nsITransferListener.h \ + nsProgressManager.h \ + progress.h \ +!endif + pw_public.h \ + $(NULL) include <$(DEPTH)/config/rules.mak> diff --git a/modules/progress/src/Makefile b/modules/progress/src/Makefile index df392a5e6a88..103f709c550e 100644 --- a/modules/progress/src/Makefile +++ b/modules/progress/src/Makefile @@ -22,7 +22,20 @@ LIBRARY_NAME = prgrss REQUIRES = progress nspr dbm img layer util +ifdef MOZ_SMOOTH_PROGRESS +REQUIRES += raptor xpcom +endif + # all code is platform specific right now CPPSRCS = pw_win.cpp pw_unix.cpp pwcommon.cpp +ifdef MOZ_SMOOTH_PROGRESS +CPPSRCS += nsProgressManager.cpp \ + nsTopProgressManager.cpp \ + nsSubProgressManager.cpp \ + nsTransfer.cpp \ + progress.cpp \ + $(NULL) +endif + include $(DEPTH)/config/rules.mk diff --git a/modules/progress/src/makefile.win b/modules/progress/src/makefile.win index 7b269bb88d9d..88a028c4d700 100644 --- a/modules/progress/src/makefile.win +++ b/modules/progress/src/makefile.win @@ -26,28 +26,63 @@ MAKE_OBJ_TYPE=EXE MODULE=progress -REQUIRES=progress nspr dbm img layer util +REQUIRES=progress nspr dbm img layer util xpcom LIBRARY_NAME=prgrss DEPTH=..\..\.. -CPPSRCS=pw_win.cpp pw_unix.cpp pwcommon.cpp -CPP_OBJS=.\$(OBJDIR)\pw_win.obj .\$(OBJDIR)\pw_unix.obj \ - .\$(OBJDIR)\pwcommon.obj + +CPPSRCS=\ + pw_win.cpp \ + pw_unix.cpp \ + pwcommon.cpp \ +!ifdef MOZ_SMOOTH_PROGRESS + nsProgressManager.cpp \ + nsSubProgressManager.cpp \ + nsTopProgressManager.cpp \ + nsTransfer.cpp \ + progress.cpp \ +!endif + $(NULL) + + +CPP_OBJS=\ + .\$(OBJDIR)\pw_win.obj \ + .\$(OBJDIR)\pw_unix.obj \ + .\$(OBJDIR)\pwcommon.obj \ +!ifdef MOZ_SMOOTH_PROGRESS + .\$(OBJDIR)\nsProgressManager.obj \ + .\$(OBJDIR)\nsTopProgressManager.obj \ + .\$(OBJDIR)\nsSubProgressManager.obj \ + .\$(OBJDIR)\nsTransfer.obj \ + .\$(OBJDIR)\progress.obj \ +!endif + $(NULL) + !if "$(MOZ_BITS)" != "16" -LINCS=-I$(XPDIST)\public\progress -I$(XPDIST)\public\nspr \ - -I$(XPDIST)\public\dbm -I$(XPDIST)\public\img \ - -I$(XPDIST)\public\layer -I$(XPDIST)\public\util +LINCS=\ + -I$(XPDIST)\public\progress \ + -I$(XPDIST)\public\nspr \ + -I$(XPDIST)\public\dbm \ + -I$(XPDIST)\public\img \ + -I$(XPDIST)\public\layer \ + -I$(XPDIST)\public\util \ +!ifdef MOZ_SMOOTH_PROGRESS + -I$(XPDIST)\public\network \ + -I$(XPDIST)\public\raptor \ + -I$(XPDIST)\public\xpcom \ +!endif + $(NULL) !endif include <$(DEPTH)\config\rules.mak> # XXX this should not be required, but mantomake doesnt grok J?I yet # also, -I..\public should be -I$(PUBLIC)\progress -LINCS= $(LINCS) \ - -I$(DEPTH)/cmd/winfe \ +LINCS=$(LINCS) \ + -I$(DEPTH)/cmd/winfe \ -I..\public libs:: $(LIBRARY) - $(MAKE_INSTALL) $(LIBRARY) $(DIST)\lib + $(MAKE_INSTALL) $(LIBRARY) $(DIST)\lib clobber:: $(RM) $(DIST)\lib\$(LIBRARY) diff --git a/modules/progress/src/nsSubProgressManager.cpp b/modules/progress/src/nsSubProgressManager.cpp new file mode 100644 index 000000000000..244cc26b2ed9 --- /dev/null +++ b/modules/progress/src/nsSubProgressManager.cpp @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#include "nsSubProgressManager.h" + + +//////////////////////////////////////////////////////////////////////// + + +nsSubProgressManager::nsSubProgressManager(MWContext* context) + : nsProgressManager(context) +{ + // Grab a reference to the parent context's progress manager + nsITransferListener* pm = fContext->grid_parent->progressManager; + PR_ASSERT(pm); + if (pm) + pm->AddRef(); +} + + +nsSubProgressManager::~nsSubProgressManager(void) +{ + // Release the reference on the parent context's progress manager + nsITransferListener* pm = fContext->grid_parent->progressManager; + if (pm) + pm->Release(); +} + + +//////////////////////////////////////////////////////////////////////// + +NS_IMETHODIMP +nsSubProgressManager::OnStartBinding(const URL_Struct* url) +{ + nsITransferListener* pm = fContext->grid_parent->progressManager; + PR_ASSERT(pm); + if (! pm) + return NS_ERROR_NULL_POINTER; + + return pm->OnStartBinding(url); +} + + +NS_IMETHODIMP +nsSubProgressManager::OnProgress(const URL_Struct* url, + PRUint32 bytesReceived, + PRUint32 contentLength) +{ + nsITransferListener* pm = fContext->grid_parent->progressManager; + PR_ASSERT(pm); + if (! pm) + return NS_ERROR_NULL_POINTER; + + return pm->OnProgress(url, bytesReceived, contentLength); +} + + +NS_IMETHODIMP +nsSubProgressManager::OnStatus(const URL_Struct* url, const char* message) +{ + nsITransferListener* pm = fContext->grid_parent->progressManager; + PR_ASSERT(pm); + if (! pm) + return NS_ERROR_NULL_POINTER; + + return pm->OnStatus(url, message); +} + + +NS_IMETHODIMP +nsSubProgressManager::OnSuspend(const URL_Struct* url) +{ + nsITransferListener* pm = fContext->grid_parent->progressManager; + PR_ASSERT(pm); + if (! pm) + return NS_ERROR_NULL_POINTER; + + return pm->OnSuspend(url); +} + + +NS_IMETHODIMP +nsSubProgressManager::OnResume(const URL_Struct* url) +{ + nsITransferListener* pm = fContext->grid_parent->progressManager; + PR_ASSERT(pm); + if (! pm) + return NS_ERROR_NULL_POINTER; + + return pm->OnResume(url); +} + + +NS_IMETHODIMP +nsSubProgressManager::OnStopBinding(const URL_Struct* url, + PRInt32 status, + const char* message) +{ + nsITransferListener* pm = fContext->grid_parent->progressManager; + PR_ASSERT(pm); + if (! pm) + return NS_ERROR_NULL_POINTER; + + return pm->OnStopBinding(url, status, message); +} + + + diff --git a/modules/progress/src/nsSubProgressManager.h b/modules/progress/src/nsSubProgressManager.h new file mode 100644 index 000000000000..91a335487834 --- /dev/null +++ b/modules/progress/src/nsSubProgressManager.h @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +#ifndef nsSubProgressManager_h__ +#define nsSubProgressManager_h__ + +#include "nsProgressManager.h" + +/** + * The nsSubProgressManager is a progress manager that gets created + * in a grid (frame cell) context. It simply delegates all of the operations + * in the nsITransferListener interface to it's grid parent's + * progress manager. + */ +class nsSubProgressManager : public nsProgressManager +{ +public: + nsSubProgressManager(MWContext* context); + virtual ~nsSubProgressManager(void); + + NS_IMETHOD + OnStartBinding(const URL_Struct* url); + + NS_IMETHOD + OnProgress(const URL_Struct* url, PRUint32 bytesReceived, PRUint32 contentLength); + + NS_IMETHOD + OnStatus(const URL_Struct* url, const char* message); + + NS_IMETHOD + OnSuspend(const URL_Struct* url); + + NS_IMETHOD + OnResume(const URL_Struct* url); + + NS_IMETHOD + OnStopBinding(const URL_Struct* url, PRInt32 status, const char* message); +}; + + +#endif // nsSubProgressManager_h__ + + diff --git a/modules/progress/src/nsTopProgressManager.cpp b/modules/progress/src/nsTopProgressManager.cpp new file mode 100644 index 000000000000..d734f47f73e6 --- /dev/null +++ b/modules/progress/src/nsTopProgressManager.cpp @@ -0,0 +1,691 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#include "nsTopProgressManager.h" +#include "nsTransfer.h" +#include "xp.h" // for FE_* callbacks +#include "xpgetstr.h" +#include "prprf.h" +#include "prmem.h" +#include "plstr.h" + +#define OBJECT_TABLE_INIT_SIZE 32 + + +//////////////////////////////////////////////////////////////////////// +// +// Debugging garbage +// +// + +#if 0 /* defined(DEBUG) */ +#define TRACE_PROGRESS(args) pm_TraceProgress args + +static void +pm_TraceProgress(const char* fmtstr, ...) +{ + char buf[256]; + va_list ap; + va_start(ap, fmtstr); + PR_vsnprintf(buf, sizeof(buf), fmtstr, ap); + va_end(ap); + +#if defined(XP_WIN) + OutputDebugString(buf); +#elif defined(XP_UNIX) +#elif defined(XP_MAC) +#endif +} + +#else /* defined(DEBUG) */ +#define TRACE_PROGRESS(args) +#endif /* defined(DEBUG) */ + + + + +//////////////////////////////////////////////////////////////////////// +// +// Hash table allocation routines +// +// + +static PR_CALLBACK void* +AllocTable(void* pool, PRSize size) +{ + return PR_MALLOC(size); +} + +static PR_CALLBACK void +FreeTable(void* pool, void* item) +{ + PR_DELETE(item); +} + +static PR_CALLBACK PLHashEntry* +AllocEntry(void* pool, const void* key) +{ + return PR_NEW(PLHashEntry); +} + +static PR_CALLBACK void +FreeEntry(void* pool, PLHashEntry* he, PRUintn flag) +{ + if (flag == HT_FREE_VALUE) { + if (he->value) { + nsTransfer* obj = (nsTransfer*) he->value; + obj->Release(); + } + } + else if (flag == HT_FREE_ENTRY) { + // we don't own the key, so leave it alone... + + if (he->value) { + nsTransfer* obj = (nsTransfer*) he->value; + obj->Release(); + } + PR_DELETE(he); + } +} + + +static PLHashAllocOps AllocOps = { + AllocTable, + FreeTable, + AllocEntry, + FreeEntry +}; + + +static PLHashNumber +pm_HashURL(const void* key) +{ + return (PLHashNumber) key; +} + + +static int +pm_CompareURLs(const void* v1, const void* v2) +{ + return v1 == v2; +} + + +//////////////////////////////////////////////////////////////////////// +// +// Hash table iterators +// +// + +static PRIntn +pm_AggregateTransferInfo(PLHashEntry* he, PRIntn i, void* closure) +{ + AggregateTransferInfo* info = (AggregateTransferInfo*) closure; + + ++(info->ObjectCount); + if (he->value) { + nsTransfer* transfer = (nsTransfer*) he->value; + + if (transfer->IsComplete()) + ++(info->CompleteCount); + + if (transfer->IsSuspended()) + ++(info->SuspendedCount); + + info->MSecRemaining += transfer->GetMSecRemaining(); + info->BytesReceived += transfer->GetBytesReceived(); + info->ContentLength += transfer->GetContentLength(); + + if (! transfer->IsComplete() && + transfer->GetContentLength() == transfer->GetBytesReceived()) + ++(info->UnknownLengthCount); + } + + return HT_ENUMERATE_NEXT; +} + + +//////////////////////////////////////////////////////////////////////// +// +// nsTopProgressManager +// +// + + +//////////////////////////////////////////////////////////////////////// + + +nsTopProgressManager::nsTopProgressManager(MWContext* context) + : nsProgressManager(context), + fActualStart(PR_Now()), + fProgressBarStart(PR_Now()), + fDefaultStatus(NULL) +{ + fURLs = PL_NewHashTable(OBJECT_TABLE_INIT_SIZE, + pm_HashURL, + pm_CompareURLs, + PL_CompareValues, + &AllocOps, + NULL); + + // Start the progress manager + fTimeout = FE_SetTimeout(nsTopProgressManager::TimeoutCallback, (void*) this, 500); + PR_ASSERT(fTimeout); + + // to avoid "strobe" mode... + fProgress = 1; + FE_SetProgressBarPercent(fContext, fProgress); +} + + +nsTopProgressManager::~nsTopProgressManager(void) +{ + if (fDefaultStatus) { + PL_strfree(fDefaultStatus); + fDefaultStatus = NULL; + } + + if (fURLs) { + PL_HashTableDestroy(fURLs); + fURLs = NULL; + } + + if (fTimeout) { + FE_ClearTimeout(fTimeout); + fTimeout = NULL; + } + + // XXX Needs to go to allxpstr.h + FE_Progress(fContext, "Done."); +} + + +//////////////////////////////////////////////////////////////////////// + +NS_IMETHODIMP +nsTopProgressManager::OnStartBinding(const URL_Struct* url) +{ + PR_ASSERT(url); + if (! url) + return NS_ERROR_NULL_POINTER; + + PR_ASSERT(fURLs); + if (! fURLs) + return NS_ERROR_NULL_POINTER; + + TRACE_PROGRESS(("OnStartBinding(%s)\n", url->address)); + + PL_HashTableAdd(fURLs, url, new nsTransfer(url)); + return NS_OK; +} + +NS_IMETHODIMP +nsTopProgressManager::OnProgress(const URL_Struct* url, + PRUint32 bytesReceived, + PRUint32 contentLength) +{ + // Some sanity checks... + PR_ASSERT(url); + if (! url) + return NS_ERROR_NULL_POINTER; + + TRACE_PROGRESS(("OnProgress(%s, %ld, %ld)\n", url->address, bytesReceived, contentLength)); + + PR_ASSERT(fURLs); + if (! fURLs) + return NS_ERROR_NULL_POINTER; + + nsTransfer* transfer = (nsTransfer*) PL_HashTableLookup(fURLs, url); + + PR_ASSERT(transfer); + if (!transfer) + return NS_ERROR_NULL_POINTER; + + transfer->SetProgress(bytesReceived, contentLength); + return NS_OK; +} + +NS_IMETHODIMP +nsTopProgressManager::OnStatus(const URL_Struct* url, const char* message) +{ + TRACE_PROGRESS(("OnStatus(%s, %s)\n", (url ? url->address : NULL), message)); + + // There are cases when transfer may be null, and that's ok. + if (url) { + PR_ASSERT(fURLs); + if (! fURLs) + return NS_ERROR_NULL_POINTER; + + nsTransfer* transfer = (nsTransfer*) PL_HashTableLookup(fURLs, url); + + PR_ASSERT(transfer); + if (transfer) + transfer->SetStatus(message); + } + + if (fDefaultStatus) + PL_strfree(fDefaultStatus); + + fDefaultStatus = PL_strdup(message); + return NS_OK; +} + + +NS_IMETHODIMP +nsTopProgressManager::OnSuspend(const URL_Struct* url) +{ + PR_ASSERT(url); + if (! url) + return NS_ERROR_NULL_POINTER; + + TRACE_PROGRESS(("OnSuspend(%s)\n", url->address)); + + PR_ASSERT(fURLs); + if (! fURLs) + return NS_ERROR_NULL_POINTER; + + nsTransfer* transfer = (nsTransfer*) PL_HashTableLookup(fURLs, url); + + PR_ASSERT(transfer); + if (!transfer) + return NS_ERROR_NULL_POINTER; + + transfer->Suspend(); + + return NS_OK; +} + + + +NS_IMETHODIMP +nsTopProgressManager::OnResume(const URL_Struct* url) +{ + PR_ASSERT(url); + if (! url) + return NS_ERROR_NULL_POINTER; + + TRACE_PROGRESS(("OnResume(%s)\n", url->address)); + + PR_ASSERT(fURLs); + if (! fURLs) + return NS_ERROR_NULL_POINTER; + + nsTransfer* transfer = (nsTransfer*) PL_HashTableLookup(fURLs, url); + + PR_ASSERT(transfer); + if (!transfer) + return NS_ERROR_NULL_POINTER; + + transfer->Resume(); + + return NS_OK; +} + + +NS_IMETHODIMP +nsTopProgressManager::OnStopBinding(const URL_Struct* url, + PRInt32 status, + const char* message) +{ + PR_ASSERT(url); + if (! url) + return NS_ERROR_NULL_POINTER; + + TRACE_PROGRESS(("OnStatus(%s, %d, %s)\n", url->address, status, message)); + + PR_ASSERT(fURLs); + if (! fURLs) + return NS_ERROR_NULL_POINTER; + + nsTransfer* transfer = (nsTransfer*) PL_HashTableLookup(fURLs, url); + + PR_ASSERT(transfer); + if (!transfer) + return NS_ERROR_NULL_POINTER; + + transfer->MarkComplete(status); + transfer->SetStatus(message); + + return NS_OK; +} + + + +//////////////////////////////////////////////////////////////////////// + +void +nsTopProgressManager::TimeoutCallback(void* closure) +{ + nsTopProgressManager* self = (nsTopProgressManager*) closure; + self->Tick(); +} + +void +nsTopProgressManager::Tick(void) +{ + TRACE_PROGRESS(("nsProgressManager.Tick: aggregating information for active objects\n")); + + AggregateTransferInfo info = { 0, 0, 0, 0, 0, 0, 0 }; + PL_HashTableEnumerateEntries(fURLs, pm_AggregateTransferInfo, (void*) &info); + + TRACE_PROGRESS(("nsProgressManager.Tick: %ld of %ld objects complete, " + "%ldms left, " + "%ld of %ld bytes xferred\n", + info.CompleteCount, info.ObjectCount, + info.MSecRemaining, + info.BytesReceived, info.ContentLength)); + + PR_ASSERT(info.ObjectCount > 0); + if (info.ObjectCount == 0) + return; + + UpdateProgressBar(info); + UpdateStatusMessage(info); + + // Check to see if we're done. + if (info.CompleteCount == info.ObjectCount) { + TRACE_PROGRESS(("Complete: %ld/%ld objects loaded\n", + info.CompleteCount, + info.ObjectCount)); + + // XXX needs to go to allxpstr.h + FE_Progress(fContext, " "); + + PL_HashTableDestroy(fURLs); + fURLs = NULL; + + fTimeout = NULL; + } + else { + // Reset the timeout to fire again... + fTimeout = FE_SetTimeout(nsTopProgressManager::TimeoutCallback, + (void*) this, 500); + } +} + + + +void +nsTopProgressManager::UpdateProgressBar(AggregateTransferInfo& info) +{ + if (info.MSecRemaining == 0) + return; + + if (info.SuspendedCount > 0) { + // turn on the strobe... + FE_SetProgressBarPercent(fContext, 0); + return; + } + + nsInt64 dt = nsTime(PR_Now()) - fProgressBarStart; + PRUint32 elapsed = dt / nsInt64((PRUint32) PR_USEC_PER_MSEC); + + // Compute the percent complete, that is, elapsed / (elapsed + remaining) + double p = ((double) elapsed) / ((double) (elapsed + info.MSecRemaining)); + PRUint32 pctComplete = (PRUint32) (100.0 * p); + +#define MONOTONIC_PROGRESS_BAR +#if defined(MONOTONIC_PROGRESS_BAR) + // This hackery is a kludge to make the progress bar + // monotonically increase rather than slipping backwards as we + // discover that there's more content to download. It works by + // adjusting the progress manager's start time backwards to + // make the elapsed time (time we've waited so far) seem + // larger in proportion to the amount of time that appears to + // be left. + if (pctComplete < fProgress) { + PRUint32 newElapsed = + (PRUint32) ((p * ((double) info.MSecRemaining)) + / (1.0 - p)); + + PRInt32 dMSec = newElapsed - elapsed; + if (dMSec > 0) + fProgressBarStart -= nsInt64(dMSec * ((PRUint32) PR_USEC_PER_MSEC)); + + // Progress bar hasn't changed -- don't bother updating it. + return; + } +#endif + + fProgress = pctComplete; + FE_SetProgressBarPercent(fContext, fProgress); +} + +//////////////////////////////////////////////////////////////////////// +// +// The bulk of the following code was pulled over from lib/xp/xp_thermo.c +// + +#ifdef XP_MAC +#include "allxpstr.h" +#else + +PR_BEGIN_EXTERN_C +extern int XP_THERMO_BYTE_FORMAT; +extern int XP_THERMO_KBYTE_FORMAT; +extern int XP_THERMO_HOURS_FORMAT; +extern int XP_THERMO_MINUTES_FORMAT; +extern int XP_THERMO_SECONDS_FORMAT; +extern int XP_THERMO_SINGULAR_FORMAT; +extern int XP_THERMO_PLURAL_FORMAT; +extern int XP_THERMO_PERCENTAGE_FORMAT; +extern int XP_THERMO_UH; +extern int XP_THERMO_PERCENT_FORM; +extern int XP_THERMO_PERCENT_RATE_FORM; +extern int XP_THERMO_RAW_COUNT_FORM; +extern int XP_THERMO_BYTE_RATE_FORMAT; +extern int XP_THERMO_K_RATE_FORMAT; +extern int XP_THERMO_M_RATE_FORMAT; +extern int XP_THERMO_STALLED_FORMAT; +extern int XP_THERMO_RATE_REMAINING_FORM; +extern int XP_THERMO_RATE_FORM; +PR_END_EXTERN_C + +#endif // XP_MAC + +#define KILOBYTE (1024L) +#define MINUTE (60L) +#define HOUR (MINUTE * MINUTE) + +#define IS_PLURAL(x) (((x) == 1) ? "" : XP_GetString(XP_THERMO_PLURAL_FORMAT)) /* L10N? */ + +#define ENOUGH_TIME_TO_GUESS 10 /* in seconds */ +#define TIME_UNTIL_DETAILS 5 + +//////////////////////////////////////////////////////////////////////// + +static void +formatRate(char* buf, PRUint32 len, double bytes_per_sec) +{ + if (bytes_per_sec > 0) { + if (bytes_per_sec < KILOBYTE) + PR_snprintf(buf, len, XP_GetString(XP_THERMO_BYTE_RATE_FORMAT), + (PRUint32) bytes_per_sec); + else + PR_snprintf(buf, len, XP_GetString(XP_THERMO_K_RATE_FORMAT), + (bytes_per_sec / ((double) KILOBYTE))); + } +} + + + +static void +formatKnownContentLength(char* buf, + PRUint32 len, + PRUint32 bytesReceived, + PRUint32 contentLength, + PRUint32 elapsed) +{ + char rate[32]; + *rate = 0; + + // the transfer rate + double bytes_per_sec = 0; + if (elapsed > 0) + bytes_per_sec = ((double) bytesReceived) / ((double) elapsed); + + formatRate(rate, sizeof(rate), bytes_per_sec); + + + // format the content length + char length[32]; + *length = 0; + + if (contentLength < KILOBYTE) + PR_snprintf(length, sizeof(length), XP_GetString(XP_THERMO_BYTE_FORMAT), contentLength); + else + PR_snprintf(length, sizeof(length), XP_GetString(XP_THERMO_KBYTE_FORMAT), contentLength / KILOBYTE); + + + // the percentage complete + char percent[32]; + + PRUint32 p = (bytesReceived * 100) / contentLength; + if (p >= 100 && bytesReceived != contentLength) + p = 99; + + PR_snprintf(percent, sizeof(percent), XP_GetString(XP_THERMO_PERCENTAGE_FORMAT), p); + + // the amount of time remaining + char tleft[32]; + *tleft = 0; + + if (bytes_per_sec >= KILOBYTE && elapsed >= ENOUGH_TIME_TO_GUESS) { + PRUint32 secs_left = + (PRUint32) (((double) (contentLength - bytesReceived)) / bytes_per_sec); + + if (secs_left >= HOUR) { + PR_snprintf(tleft, sizeof(tleft), + XP_GetString(XP_THERMO_HOURS_FORMAT), + secs_left / HOUR, + (secs_left / MINUTE) % MINUTE, + secs_left % MINUTE); + } + else if (secs_left >= MINUTE) { + PR_snprintf(tleft, sizeof(tleft), + XP_GetString(XP_THERMO_MINUTES_FORMAT), + secs_left / MINUTE, + secs_left % MINUTE); + } + else if (secs_left > 0) { + PR_snprintf(tleft, sizeof(tleft), + XP_GetString(XP_THERMO_SECONDS_FORMAT), + secs_left, + IS_PLURAL(secs_left)); + } + } + + if (*tleft) { + /* "%s of %s (at %s, %s remaining)" */ + PR_snprintf(buf, len, + XP_GetString(XP_THERMO_RATE_REMAINING_FORM), + percent, length, rate, tleft); + } + else if (*rate) { + /* "%s of %s (at %s)" */ + PR_snprintf(buf, len, + XP_GetString(XP_THERMO_RATE_FORM), + percent, length, rate); + } + else { + /* "%s of %s" */ + PR_snprintf(buf, len, + XP_GetString(XP_THERMO_PERCENT_FORM), + percent, length); + } +} + + +static void +formatUnknownContentLength(char* buf, PRUint32 len, PRUint32 bytesReceived, PRUint32 elapsed) +{ + char rate[32]; + *rate = 0; + + // the transfer rate + double bytes_per_sec = 0; + if (elapsed > 0) + bytes_per_sec = ((double) bytesReceived) / ((double) elapsed); + + formatRate(rate, sizeof(rate), bytes_per_sec); + + // the number of bytes received + char bytes_received[32]; + + if (bytesReceived < KILOBYTE) + PR_snprintf(bytes_received, sizeof(bytes_received), + XP_GetString(XP_THERMO_UH), + bytesReceived, IS_PLURAL(bytesReceived)); + else + PR_snprintf(bytes_received, sizeof(bytes_received), + XP_GetString(XP_THERMO_KBYTE_FORMAT), + bytesReceived / KILOBYTE); + + if (*rate) { + /* "%s read (at %s)" */ + PR_snprintf(buf, len, XP_GetString(XP_THERMO_PERCENT_RATE_FORM), bytes_received, rate); + } + else { + PR_snprintf(buf, len, XP_GetString(XP_THERMO_RAW_COUNT_FORM), bytes_received); + } +} + + +void +nsTopProgressManager::UpdateStatusMessage(AggregateTransferInfo& info) +{ + // Compute how much time has elapsed + nsInt64 dt = nsTime(PR_Now()) - fActualStart; + PRUint32 elapsed = dt / nsInt64((PRUint32) PR_USEC_PER_SEC); + + char buf[256]; + *buf = 0; + + if (info.ObjectCount == 1 || info.CompleteCount == 0) { + // If we only have one object that we're transferring, or if + // nothing has completed yet, show the default status message + PL_strncpy(buf, fDefaultStatus, sizeof(buf)); + } + + if (elapsed > TIME_UNTIL_DETAILS) { + char details[256]; + *details = 0; + + if (!info.UnknownLengthCount && info.ContentLength > 0) { + formatKnownContentLength(details, sizeof(details), + info.BytesReceived, + info.ContentLength, + elapsed); + } + else if (info.BytesReceived > 0) { + formatUnknownContentLength(details, sizeof(details), + info.BytesReceived, + elapsed); + } + + if (*details) { + // XXX needs to go to allxpstr.h + if (*buf) + PL_strcatn(buf, sizeof(buf), ", "); + + PL_strcatn(buf, sizeof(buf), details); + } + } + + FE_Progress(fContext, buf); +} diff --git a/modules/progress/src/nsTopProgressManager.h b/modules/progress/src/nsTopProgressManager.h new file mode 100644 index 000000000000..aa0c4726b71d --- /dev/null +++ b/modules/progress/src/nsTopProgressManager.h @@ -0,0 +1,130 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +#ifndef nsTopProgressManager_h__ +#define nsTopProgressManager_h__ + +#include "nsProgressManager.h" +#include "nsTime.h" + +struct AggregateTransferInfo { + PRUint32 ObjectCount; + PRUint32 CompleteCount; + PRUint32 SuspendedCount; + PRUint32 MSecRemaining; + PRUint32 BytesReceived; + PRUint32 ContentLength; + PRUint32 UnknownLengthCount; +}; + + + +/** + * An nsTopProgressManager is a progress manager that is associated + * with a top-level window context. It does the dirty work of managing the + * progress bar and the status text. + */ +class nsTopProgressManager : public nsProgressManager +{ +protected: + /** + * A table of URLs that we are monitoring progress for. The table is + * keyed by URL_Structs, the values are nsTransfer + * objects. + */ + PLHashTable* fURLs; + + /** + * The time at which the progress manager was created. + */ + nsTime fActualStart; + + /** + * The time at which the progress bar thinks we were created. This is used + * with to make progress seem "monotonic": it is "slipped" backwards + * when a transfer stalls so that the percentage complete stays constant. + */ + nsTime fProgressBarStart; + + /** + * The current amount of progress shown on the chrome's progress bar + */ + PRUint16 fProgress; + + /** + * The default status message to display if nothing more appropriate + * is around. + */ + char* fDefaultStatus; + + /** + * An FE timeout object that is used to periodically update the + * status bar. + */ + void* fTimeout; + + /** + * The FE timeout callback. + */ + static void TimeoutCallback(void* self); + + /** + * Called to update the status bar. + */ + virtual void Tick(void); + + + /** + * Update the progress bar + */ + virtual void UpdateProgressBar(AggregateTransferInfo& info); + + + /** + * Update the status message + */ + virtual void UpdateStatusMessage(AggregateTransferInfo& info); + + +public: + nsTopProgressManager(MWContext* context); + virtual ~nsTopProgressManager(void); + + NS_IMETHOD + OnStartBinding(const URL_Struct* url); + + NS_IMETHOD + OnProgress(const URL_Struct* url, PRUint32 bytesReceived, PRUint32 contentLength); + + NS_IMETHOD + OnStatus(const URL_Struct* url, const char* message); + + NS_IMETHOD + OnSuspend(const URL_Struct* url); + + NS_IMETHOD + OnResume(const URL_Struct* url); + + NS_IMETHOD + OnStopBinding(const URL_Struct* url, PRInt32 status, const char* message); +}; + + +#endif // nsTopProgressManager_h__ + diff --git a/modules/progress/src/nsTransfer.cpp b/modules/progress/src/nsTransfer.cpp new file mode 100644 index 000000000000..111c1df99f3c --- /dev/null +++ b/modules/progress/src/nsTransfer.cpp @@ -0,0 +1,192 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#include "nsTransfer.h" +#include "plstr.h" +#include "prtime.h" + +//////////////////////////////////////////////////////////////////////// + +PRUint32 nsTransfer::DefaultMSecRemaining = 1000; + +nsTransfer::nsTransfer(const URL_Struct* url) + : fURL(url), + fBytesReceived(0), + fContentLength(0), + fStart(PR_Now()), + fIsSuspended(PR_FALSE), + fStatus(NULL), + fComplete(PR_FALSE), + fResultCode(0) +{ +} + + +nsTransfer::~nsTransfer(void) +{ + if (fStatus) { + PL_strfree(fStatus); + fStatus = NULL; + } +} + + +NS_IMPL_ISUPPORTS(nsTransfer, kISupportsIID); + + +void +nsTransfer::SetProgress(PRUint32 bytesReceived, PRUint32 contentLength) +{ + fBytesReceived = bytesReceived; + fContentLength = contentLength; + if (fContentLength < fBytesReceived) + fContentLength = fBytesReceived; +} + + +PRUint32 +nsTransfer::GetBytesReceived(void) +{ + return fBytesReceived; +} + + +PRUint32 +nsTransfer::GetContentLength(void) +{ + return fContentLength; +} + + +double +nsTransfer::GetTransferRate(void) +{ + if (fContentLength == 0 || fBytesReceived == 0) + return 0; + + nsInt64 dt = nsTime(PR_Now()) - fStart; + PRUint32 dtMSec = dt / nsInt64((PRUint32) PR_USEC_PER_MSEC); + + if (dtMSec == 0) + return 0; + + return ((double) fBytesReceived) / ((double) dtMSec); +} + +PRUint32 +nsTransfer::GetMSecRemaining(void) +{ + if (IsComplete()) + return 0; + + if (fContentLength == 0 || fBytesReceived == 0) + // no content length and/or no bytes received + return DefaultMSecRemaining; + + PRUint32 cbRemaining = fContentLength - fBytesReceived; + if (cbRemaining == 0) + // not complete, but content length == bytes received. + return DefaultMSecRemaining; + + double bytesPerMSec = GetTransferRate(); + if (bytesPerMSec == 0) + return DefaultMSecRemaining; + + PRUint32 msecRemaining = (PRUint32) (((double) cbRemaining) / bytesPerMSec); + + return msecRemaining; +} + + + +void +nsTransfer::SetStatus(const char* message) +{ + if (fStatus) + PL_strfree(fStatus); + + fStatus = PL_strdup(message); +} + + +const char* +nsTransfer::GetStatus(void) +{ + return fStatus; +} + + + +void +nsTransfer::Suspend(void) +{ + if (fIsSuspended) + return; + + fSuspendStart = PR_Now(); +} + + + +void +nsTransfer::Resume(void) +{ + if (! fIsSuspended) + return; + + nsInt64 dt = nsTime(PR_Now()) - fSuspendStart; + fStart += dt; +} + + +PRBool +nsTransfer::IsSuspended(void) +{ + return fIsSuspended; +} + + +void +nsTransfer::MarkComplete(PRInt32 resultCode) +{ + fComplete = PR_TRUE; + fResultCode = resultCode; + + if (fResultCode >= 0) { + nsInt64 elapsed = nsTime(PR_Now()) - fStart; + PRUint32 elapsedMSec= (PRUint32) (elapsed / nsInt64((PRUint32) PR_USEC_PER_MSEC)); + UpdateDefaultMSecRemaining(elapsedMSec); + } +} + + +PRBool +nsTransfer::IsComplete(void) +{ + return fComplete; +} + + +void +nsTransfer::UpdateDefaultMSecRemaining(PRUint32 msec) +{ + // very simple. just maintain a rolling average. + DefaultMSecRemaining += msec; + DefaultMSecRemaining /= 2; +} + diff --git a/modules/progress/src/nsTransfer.h b/modules/progress/src/nsTransfer.h new file mode 100644 index 000000000000..1a96f9c2192d --- /dev/null +++ b/modules/progress/src/nsTransfer.h @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#ifndef nsTransfer_h__ +#define nsTransfer_h__ + +#include "prtypes.h" +#include "structs.h" +#include "nsISupports.h" +#include "nsTime.h" + +/** + * This class encapsulates the transfer state for each stream that will + * be transferring data, is currently transferring data, or has previously + * transferred data in the current context. + */ +class nsTransfer +{ +protected: + const URL_Struct* fURL; + PRUint32 fBytesReceived; + PRUint32 fContentLength; + nsTime fStart; + nsTime fSuspendStart; + PRBool fIsSuspended; + char* fStatus; + PRBool fComplete; + PRInt32 fResultCode; + + static PRUint32 DefaultMSecRemaining; + static void UpdateDefaultMSecRemaining(PRUint32 time); + +public: + nsTransfer(const URL_Struct* url); + virtual ~nsTransfer(void); + + NS_DECL_ISUPPORTS + + void SetProgress(PRUint32 bytesReceived, PRUint32 contentLength); + PRUint32 GetBytesReceived(void); + PRUint32 GetContentLength(void); + double GetTransferRate(void); + PRUint32 GetMSecRemaining(void); + void SetStatus(const char* message); + const char* GetStatus(void); + void Suspend(void); + void Resume(void); + PRBool IsSuspended(void); + void MarkComplete(PRInt32 resultCode); + PRBool IsComplete(void); +}; + + +#endif // nsTransfer_h__ + +