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__
+
+