michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsBrowserStatusFilter.h" michael@0: #include "nsIChannel.h" michael@0: #include "nsITimer.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsString.h" michael@0: michael@0: // XXX michael@0: // XXX DO NOT TOUCH THIS CODE UNLESS YOU KNOW WHAT YOU'RE DOING !!! michael@0: // XXX michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsBrowserStatusFilter michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: nsBrowserStatusFilter::nsBrowserStatusFilter() michael@0: : mCurProgress(0) michael@0: , mMaxProgress(0) michael@0: , mStatusIsDirty(true) michael@0: , mCurrentPercentage(0) michael@0: , mTotalRequests(0) michael@0: , mFinishedRequests(0) michael@0: , mUseRealProgressFlag(false) michael@0: , mDelayedStatus(false) michael@0: , mDelayedProgress(false) michael@0: { michael@0: } michael@0: michael@0: nsBrowserStatusFilter::~nsBrowserStatusFilter() michael@0: { michael@0: if (mTimer) { michael@0: mTimer->Cancel(); michael@0: } michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsBrowserStatusFilter::nsISupports michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMPL_ISUPPORTS(nsBrowserStatusFilter, michael@0: nsIWebProgress, michael@0: nsIWebProgressListener, michael@0: nsIWebProgressListener2, michael@0: nsISupportsWeakReference) michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsBrowserStatusFilter::nsIWebProgress michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: nsBrowserStatusFilter::AddProgressListener(nsIWebProgressListener *aListener, michael@0: uint32_t aNotifyMask) michael@0: { michael@0: mListener = aListener; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBrowserStatusFilter::RemoveProgressListener(nsIWebProgressListener *aListener) michael@0: { michael@0: if (aListener == mListener) michael@0: mListener = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBrowserStatusFilter::GetDOMWindow(nsIDOMWindow **aResult) michael@0: { michael@0: NS_NOTREACHED("nsBrowserStatusFilter::GetDOMWindow"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBrowserStatusFilter::GetDOMWindowID(uint64_t *aResult) michael@0: { michael@0: *aResult = 0; michael@0: NS_NOTREACHED("nsBrowserStatusFilter::GetDOMWindowID"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBrowserStatusFilter::GetIsTopLevel(bool *aIsTopLevel) michael@0: { michael@0: *aIsTopLevel = false; michael@0: NS_NOTREACHED("nsBrowserStatusFilter::GetIsTopLevel"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBrowserStatusFilter::GetIsLoadingDocument(bool *aIsLoadingDocument) michael@0: { michael@0: NS_NOTREACHED("nsBrowserStatusFilter::GetIsLoadingDocument"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBrowserStatusFilter::GetLoadType(uint32_t *aLoadType) michael@0: { michael@0: *aLoadType = 0; michael@0: NS_NOTREACHED("nsBrowserStatusFilter::GetLoadType"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsBrowserStatusFilter::nsIWebProgressListener michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: nsBrowserStatusFilter::OnStateChange(nsIWebProgress *aWebProgress, michael@0: nsIRequest *aRequest, michael@0: uint32_t aStateFlags, michael@0: nsresult aStatus) michael@0: { michael@0: if (!mListener) michael@0: return NS_OK; michael@0: michael@0: if (aStateFlags & STATE_START) { michael@0: if (aStateFlags & STATE_IS_NETWORK) { michael@0: ResetMembers(); michael@0: } michael@0: if (aStateFlags & STATE_IS_REQUEST) { michael@0: ++mTotalRequests; michael@0: michael@0: // if the total requests exceeds 1, then we'll base our progress michael@0: // notifications on the percentage of completed requests. michael@0: // otherwise, progress for the single request will be reported. michael@0: mUseRealProgressFlag = (mTotalRequests == 1); michael@0: } michael@0: } michael@0: else if (aStateFlags & STATE_STOP) { michael@0: if (aStateFlags & STATE_IS_REQUEST) { michael@0: ++mFinishedRequests; michael@0: // Note: Do not return from here. This is necessary so that the michael@0: // STATE_STOP can still be relayed to the listener if needed michael@0: // (bug 209330) michael@0: if (!mUseRealProgressFlag && mTotalRequests) michael@0: OnProgressChange(nullptr, nullptr, 0, 0, michael@0: mFinishedRequests, mTotalRequests); michael@0: } michael@0: } michael@0: else if (aStateFlags & STATE_TRANSFERRING) { michael@0: if (aStateFlags & STATE_IS_REQUEST) { michael@0: if (!mUseRealProgressFlag && mTotalRequests) michael@0: return OnProgressChange(nullptr, nullptr, 0, 0, michael@0: mFinishedRequests, mTotalRequests); michael@0: } michael@0: michael@0: // no need to forward this state change michael@0: return NS_OK; michael@0: } else { michael@0: // no need to forward this state change michael@0: return NS_OK; michael@0: } michael@0: michael@0: // If we're here, we have either STATE_START or STATE_STOP. The michael@0: // listener only cares about these in certain conditions. michael@0: bool isLoadingDocument = false; michael@0: if ((aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK || michael@0: (aStateFlags & nsIWebProgressListener::STATE_IS_REQUEST && michael@0: mFinishedRequests == mTotalRequests && michael@0: (aWebProgress->GetIsLoadingDocument(&isLoadingDocument), michael@0: !isLoadingDocument)))) { michael@0: if (mTimer && (aStateFlags & nsIWebProgressListener::STATE_STOP)) { michael@0: mTimer->Cancel(); michael@0: ProcessTimeout(); michael@0: } michael@0: michael@0: return mListener->OnStateChange(aWebProgress, aRequest, aStateFlags, michael@0: aStatus); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBrowserStatusFilter::OnProgressChange(nsIWebProgress *aWebProgress, michael@0: nsIRequest *aRequest, michael@0: int32_t aCurSelfProgress, michael@0: int32_t aMaxSelfProgress, michael@0: int32_t aCurTotalProgress, michael@0: int32_t aMaxTotalProgress) michael@0: { michael@0: if (!mListener) michael@0: return NS_OK; michael@0: michael@0: if (!mUseRealProgressFlag && aRequest) michael@0: return NS_OK; michael@0: michael@0: // michael@0: // limit frequency of calls to OnProgressChange michael@0: // michael@0: michael@0: mCurProgress = (int64_t)aCurTotalProgress; michael@0: mMaxProgress = (int64_t)aMaxTotalProgress; michael@0: michael@0: if (mDelayedProgress) michael@0: return NS_OK; michael@0: michael@0: if (!mDelayedStatus) { michael@0: MaybeSendProgress(); michael@0: StartDelayTimer(); michael@0: } michael@0: michael@0: mDelayedProgress = true; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBrowserStatusFilter::OnLocationChange(nsIWebProgress *aWebProgress, michael@0: nsIRequest *aRequest, michael@0: nsIURI *aLocation, michael@0: uint32_t aFlags) michael@0: { michael@0: if (!mListener) michael@0: return NS_OK; michael@0: michael@0: return mListener->OnLocationChange(aWebProgress, aRequest, aLocation, michael@0: aFlags); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBrowserStatusFilter::OnStatusChange(nsIWebProgress *aWebProgress, michael@0: nsIRequest *aRequest, michael@0: nsresult aStatus, michael@0: const char16_t *aMessage) michael@0: { michael@0: if (!mListener) michael@0: return NS_OK; michael@0: michael@0: // michael@0: // limit frequency of calls to OnStatusChange michael@0: // michael@0: if (mStatusIsDirty || !mCurrentStatusMsg.Equals(aMessage)) { michael@0: mStatusIsDirty = true; michael@0: mStatusMsg = aMessage; michael@0: } michael@0: michael@0: if (mDelayedStatus) michael@0: return NS_OK; michael@0: michael@0: if (!mDelayedProgress) { michael@0: MaybeSendStatus(); michael@0: StartDelayTimer(); michael@0: } michael@0: michael@0: mDelayedStatus = true; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBrowserStatusFilter::OnSecurityChange(nsIWebProgress *aWebProgress, michael@0: nsIRequest *aRequest, michael@0: uint32_t aState) michael@0: { michael@0: if (!mListener) michael@0: return NS_OK; michael@0: michael@0: return mListener->OnSecurityChange(aWebProgress, aRequest, aState); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsBrowserStatusFilter::nsIWebProgressListener2 michael@0: //----------------------------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsBrowserStatusFilter::OnProgressChange64(nsIWebProgress *aWebProgress, michael@0: nsIRequest *aRequest, michael@0: int64_t aCurSelfProgress, michael@0: int64_t aMaxSelfProgress, michael@0: int64_t aCurTotalProgress, michael@0: int64_t aMaxTotalProgress) michael@0: { michael@0: // XXX truncates 64-bit to 32-bit michael@0: return OnProgressChange(aWebProgress, aRequest, michael@0: (int32_t)aCurSelfProgress, michael@0: (int32_t)aMaxSelfProgress, michael@0: (int32_t)aCurTotalProgress, michael@0: (int32_t)aMaxTotalProgress); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBrowserStatusFilter::OnRefreshAttempted(nsIWebProgress *aWebProgress, michael@0: nsIURI *aUri, michael@0: int32_t aDelay, michael@0: bool aSameUri, michael@0: bool *allowRefresh) michael@0: { michael@0: nsCOMPtr listener = michael@0: do_QueryInterface(mListener); michael@0: if (!listener) { michael@0: *allowRefresh = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: return listener->OnRefreshAttempted(aWebProgress, aUri, aDelay, aSameUri, michael@0: allowRefresh); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsBrowserStatusFilter michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: void michael@0: nsBrowserStatusFilter::ResetMembers() michael@0: { michael@0: mTotalRequests = 0; michael@0: mFinishedRequests = 0; michael@0: mUseRealProgressFlag = false; michael@0: mMaxProgress = 0; michael@0: mCurProgress = 0; michael@0: mCurrentPercentage = 0; michael@0: mStatusIsDirty = true; michael@0: } michael@0: michael@0: void michael@0: nsBrowserStatusFilter::MaybeSendProgress() michael@0: { michael@0: if (mCurProgress > mMaxProgress || mCurProgress <= 0) michael@0: return; michael@0: michael@0: // check our percentage michael@0: int32_t percentage = (int32_t) double(mCurProgress) * 100 / mMaxProgress; michael@0: michael@0: // The progress meter only updates for increases greater than 3 percent michael@0: if (percentage > (mCurrentPercentage + 3)) { michael@0: mCurrentPercentage = percentage; michael@0: // XXX truncates 64-bit to 32-bit michael@0: mListener->OnProgressChange(nullptr, nullptr, 0, 0, michael@0: (int32_t)mCurProgress, michael@0: (int32_t)mMaxProgress); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsBrowserStatusFilter::MaybeSendStatus() michael@0: { michael@0: if (mStatusIsDirty) { michael@0: mListener->OnStatusChange(nullptr, nullptr, NS_OK, mStatusMsg.get()); michael@0: mCurrentStatusMsg = mStatusMsg; michael@0: mStatusIsDirty = false; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsBrowserStatusFilter::StartDelayTimer() michael@0: { michael@0: NS_ASSERTION(!DelayInEffect(), "delay should not be in effect"); michael@0: michael@0: mTimer = do_CreateInstance("@mozilla.org/timer;1"); michael@0: if (!mTimer) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return mTimer->InitWithFuncCallback(TimeoutHandler, this, 160, michael@0: nsITimer::TYPE_ONE_SHOT); michael@0: } michael@0: michael@0: void michael@0: nsBrowserStatusFilter::ProcessTimeout() michael@0: { michael@0: mTimer = nullptr; michael@0: michael@0: if (!mListener) michael@0: return; michael@0: michael@0: if (mDelayedStatus) { michael@0: mDelayedStatus = false; michael@0: MaybeSendStatus(); michael@0: } michael@0: michael@0: if (mDelayedProgress) { michael@0: mDelayedProgress = false; michael@0: MaybeSendProgress(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsBrowserStatusFilter::TimeoutHandler(nsITimer *aTimer, void *aClosure) michael@0: { michael@0: nsBrowserStatusFilter *self = reinterpret_cast(aClosure); michael@0: if (!self) { michael@0: NS_ERROR("no self"); michael@0: return; michael@0: } michael@0: michael@0: self->ProcessTimeout(); michael@0: }