ProgressTracker.cpp [plain text]
#include "config.h"
#include "ProgressTracker.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "FrameLoaderClient.h"
#include "ResourceResponse.h"
#include "SystemTime.h"
using std::min;
namespace WebCore {
static const double initialProgressValue = 0.1;
static const double finalProgressValue = 0.9;
static const int progressItemDefaultEstimatedLength = 1024 * 16;
struct ProgressItem {
ProgressItem(long long length)
: bytesReceived(0)
, estimatedLength(length) { }
long long bytesReceived;
long long estimatedLength;
};
ProgressTracker::ProgressTracker()
: m_uniqueIdentifier(0)
, m_totalPageAndResourceBytesToLoad(0)
, m_totalBytesReceived(0)
, m_lastNotifiedProgressValue(0)
, m_lastNotifiedProgressTime(0)
, m_progressNotificationInterval(0.02)
, m_progressNotificationTimeInterval(0.1)
, m_finalProgressChangedSent(false)
, m_progressValue(0)
, m_numProgressTrackedFrames(0)
{
}
ProgressTracker::~ProgressTracker()
{
deleteAllValues(m_progressItems);
}
double ProgressTracker::estimatedProgress() const
{
return m_progressValue;
}
void ProgressTracker::reset()
{
deleteAllValues(m_progressItems);
m_progressItems.clear();
m_totalPageAndResourceBytesToLoad = 0;
m_totalBytesReceived = 0;
m_progressValue = 0;
m_lastNotifiedProgressValue = 0;
m_lastNotifiedProgressTime = 0;
m_finalProgressChangedSent = false;
m_numProgressTrackedFrames = 0;
m_originatingProgressFrame = 0;
}
void ProgressTracker::progressStarted(Frame* frame)
{
frame->loader()->client()->willChangeEstimatedProgress();
if (m_numProgressTrackedFrames == 0 || m_originatingProgressFrame == frame) {
reset();
m_progressValue = initialProgressValue;
m_originatingProgressFrame = frame;
m_originatingProgressFrame->loader()->client()->postProgressStartedNotification();
}
m_numProgressTrackedFrames++;
frame->loader()->client()->didChangeEstimatedProgress();
}
void ProgressTracker::progressCompleted(Frame* frame)
{
if (m_numProgressTrackedFrames <= 0)
return;
frame->loader()->client()->willChangeEstimatedProgress();
m_numProgressTrackedFrames--;
if (m_numProgressTrackedFrames == 0 ||
(frame == m_originatingProgressFrame && m_numProgressTrackedFrames != 0))
finalProgressComplete();
frame->loader()->client()->didChangeEstimatedProgress();
}
void ProgressTracker::finalProgressComplete()
{
RefPtr<Frame> frame = m_originatingProgressFrame.release();
if (!m_finalProgressChangedSent) {
m_progressValue = 1;
frame->loader()->client()->postProgressEstimateChangedNotification();
}
reset();
frame->loader()->client()->setMainFrameDocumentReady(true);
frame->loader()->client()->postProgressFinishedNotification();
}
void ProgressTracker::incrementProgress(unsigned long identifier, const ResourceResponse& response)
{
if (m_numProgressTrackedFrames <= 0)
return;
long long estimatedLength = response.expectedContentLength();
if (estimatedLength < 0)
estimatedLength = progressItemDefaultEstimatedLength;
m_totalPageAndResourceBytesToLoad += estimatedLength;
if (ProgressItem* item = m_progressItems.get(identifier)) {
item->bytesReceived = 0;
item->estimatedLength = estimatedLength;
} else
m_progressItems.set(identifier, new ProgressItem(estimatedLength));
}
void ProgressTracker::incrementProgress(unsigned long identifier, const char*, int length)
{
ProgressItem* item = m_progressItems.get(identifier);
if (!item)
return;
m_originatingProgressFrame->loader()->client()->willChangeEstimatedProgress();
unsigned bytesReceived = length;
double increment, percentOfRemainingBytes;
long long remainingBytes, estimatedBytesForPendingRequests;
item->bytesReceived += bytesReceived;
if (item->bytesReceived > item->estimatedLength) {
m_totalPageAndResourceBytesToLoad += ((item->bytesReceived * 2) - item->estimatedLength);
item->estimatedLength = item->bytesReceived * 2;
}
int numPendingOrLoadingRequests = m_originatingProgressFrame->loader()->numPendingOrLoadingRequests(true);
estimatedBytesForPendingRequests = progressItemDefaultEstimatedLength * numPendingOrLoadingRequests;
remainingBytes = ((m_totalPageAndResourceBytesToLoad + estimatedBytesForPendingRequests) - m_totalBytesReceived);
if (remainingBytes > 0) percentOfRemainingBytes = (double)bytesReceived / (double)remainingBytes;
else
percentOfRemainingBytes = 1.0;
double maxProgressValue = m_originatingProgressFrame->loader()->firstLayoutDone() ? finalProgressValue : .5;
increment = (maxProgressValue - m_progressValue) * percentOfRemainingBytes;
m_progressValue += increment;
m_progressValue = min(m_progressValue, maxProgressValue);
ASSERT(m_progressValue >= initialProgressValue);
m_totalBytesReceived += bytesReceived;
double now = currentTime();
double notifiedProgressTimeDelta = now - m_lastNotifiedProgressTime;
double notificationProgressDelta = m_progressValue - m_lastNotifiedProgressValue;
if ((notificationProgressDelta >= m_progressNotificationInterval ||
notifiedProgressTimeDelta >= m_progressNotificationTimeInterval) &&
m_numProgressTrackedFrames > 0) {
if (!m_finalProgressChangedSent) {
if (m_progressValue == 1)
m_finalProgressChangedSent = true;
m_originatingProgressFrame->loader()->client()->postProgressEstimateChangedNotification();
m_lastNotifiedProgressValue = m_progressValue;
m_lastNotifiedProgressTime = now;
}
}
if (m_originatingProgressFrame && m_originatingProgressFrame->loader() && m_originatingProgressFrame->loader()->client())
m_originatingProgressFrame->loader()->client()->didChangeEstimatedProgress();
}
void ProgressTracker::completeProgress(unsigned long identifier)
{
ProgressItem* item = m_progressItems.get(identifier);
if (!item)
return;
long long delta = item->bytesReceived - item->estimatedLength;
m_totalPageAndResourceBytesToLoad += delta;
item->estimatedLength = item->bytesReceived;
m_progressItems.remove(identifier);
delete item;
}
unsigned long ProgressTracker::createUniqueIdentifier()
{
return ++m_uniqueIdentifier;
}
}