diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /toolkit/components/statusfilter | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/statusfilter')
-rw-r--r-- | toolkit/components/statusfilter/moz.build | 18 | ||||
-rw-r--r-- | toolkit/components/statusfilter/nsBrowserStatusFilter.cpp | 362 | ||||
-rw-r--r-- | toolkit/components/statusfilter/nsBrowserStatusFilter.h | 78 |
3 files changed, 458 insertions, 0 deletions
diff --git a/toolkit/components/statusfilter/moz.build b/toolkit/components/statusfilter/moz.build new file mode 100644 index 0000000000..50610a5442 --- /dev/null +++ b/toolkit/components/statusfilter/moz.build @@ -0,0 +1,18 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +with Files("**"): + BUG_COMPONENT = ("Core", "DOM: Navigation") + +SOURCES += [ + "nsBrowserStatusFilter.cpp", +] + +EXPORTS += [ + "nsBrowserStatusFilter.h", +] + +FINAL_LIBRARY = "xul" diff --git a/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp b/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp new file mode 100644 index 0000000000..654903fadb --- /dev/null +++ b/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp @@ -0,0 +1,362 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsBrowserStatusFilter.h" +#include "nsITimer.h" +#include "nsString.h" +#include "nsThreadUtils.h" + +using namespace mozilla; + +//----------------------------------------------------------------------------- +// nsBrowserStatusFilter <public> +//----------------------------------------------------------------------------- + +nsBrowserStatusFilter::nsBrowserStatusFilter() + : mTarget(GetMainThreadSerialEventTarget()), + mCurProgress(0), + mMaxProgress(0), + mCurrentPercentage(0), + mStatusIsDirty(true), + mIsLoadingDocument(false), + mDelayedStatus(false), + mDelayedProgress(false) {} + +nsBrowserStatusFilter::~nsBrowserStatusFilter() { + if (mTimer) { + mTimer->Cancel(); + } +} + +//----------------------------------------------------------------------------- +// nsBrowserStatusFilter::nsISupports +//----------------------------------------------------------------------------- + +NS_IMPL_CYCLE_COLLECTION_WEAK(nsBrowserStatusFilter, mListener, mTarget) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsBrowserStatusFilter) + NS_INTERFACE_MAP_ENTRY(nsIWebProgress) + NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener) + NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener2) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebProgress) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsBrowserStatusFilter) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsBrowserStatusFilter) + +//----------------------------------------------------------------------------- +// nsBrowserStatusFilter::nsIWebProgress +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsBrowserStatusFilter::AddProgressListener(nsIWebProgressListener* aListener, + uint32_t aNotifyMask) { + mListener = aListener; + return NS_OK; +} + +NS_IMETHODIMP +nsBrowserStatusFilter::RemoveProgressListener( + nsIWebProgressListener* aListener) { + if (aListener == mListener) mListener = nullptr; + return NS_OK; +} + +NS_IMETHODIMP +nsBrowserStatusFilter::GetBrowsingContextXPCOM( + mozilla::dom::BrowsingContext** aResult) { + MOZ_ASSERT_UNREACHABLE("nsBrowserStatusFilter::GetBrowsingContextXPCOM"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +mozilla::dom::BrowsingContext* nsBrowserStatusFilter::GetBrowsingContext() { + MOZ_ASSERT_UNREACHABLE("nsBrowserStatusFilter::GetBrowsingContext"); + return nullptr; +} + +NS_IMETHODIMP +nsBrowserStatusFilter::GetDOMWindow(mozIDOMWindowProxy** aResult) { + MOZ_ASSERT_UNREACHABLE("nsBrowserStatusFilter::GetDOMWindow"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsBrowserStatusFilter::GetIsTopLevel(bool* aIsTopLevel) { + *aIsTopLevel = false; + MOZ_ASSERT_UNREACHABLE("nsBrowserStatusFilter::GetIsTopLevel"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsBrowserStatusFilter::GetIsLoadingDocument(bool* aIsLoadingDocument) { + MOZ_ASSERT_UNREACHABLE("nsBrowserStatusFilter::GetIsLoadingDocument"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsBrowserStatusFilter::GetLoadType(uint32_t* aLoadType) { + *aLoadType = 0; + MOZ_ASSERT_UNREACHABLE("nsBrowserStatusFilter::GetLoadType"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsBrowserStatusFilter::GetTarget(nsIEventTarget** aTarget) { + nsCOMPtr<nsIEventTarget> target = mTarget; + target.forget(aTarget); + return NS_OK; +} + +NS_IMETHODIMP +nsBrowserStatusFilter::SetTarget(nsIEventTarget* aTarget) { + mTarget = aTarget; + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsBrowserStatusFilter::nsIWebProgressListener +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsBrowserStatusFilter::OnStateChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, uint32_t aStateFlags, + nsresult aStatus) { + if (!mListener) return NS_OK; + + if (aStateFlags & STATE_START) { + // Reset members on beginning of document loading, but we don't want + // subframe document loading followed by the root document loading + // resets members accidentally, so for non-toplevel load we check if + // there hasn't been a document load started. + if (aStateFlags & STATE_IS_DOCUMENT) { + bool isTopLevel = false; + aWebProgress->GetIsTopLevel(&isTopLevel); + if (!mIsLoadingDocument || isTopLevel) { + ResetMembers(); + } + mIsLoadingDocument = true; + } + } else if (aStateFlags & STATE_STOP) { + // Flush pending status / progress update during document loading. + if (mIsLoadingDocument) { + bool isLoadingDocument = true; + aWebProgress->GetIsLoadingDocument(&isLoadingDocument); + mIsLoadingDocument &= isLoadingDocument; + + if (mTimer) { + mTimer->Cancel(); + CallDelayedProgressListeners(); + + // CallDelayedProgressListeners() may trigger OnStatusChange and/or + // OnProgressChange handlers, which can run JS and may even call + // RemoveProgressListener. + if (!mListener) { + return NS_OK; + } + } + } + } else { + // No need to forward this state change. + return NS_OK; + } + + // Only notify listener for STATE_IS_NETWORK or STATE_IS_REDIRECTED_DOCUMENT + if (aStateFlags & STATE_IS_NETWORK || + aStateFlags & STATE_IS_REDIRECTED_DOCUMENT) { + return mListener->OnStateChange(aWebProgress, aRequest, aStateFlags, + aStatus); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsBrowserStatusFilter::OnProgressChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + int32_t aCurSelfProgress, + int32_t aMaxSelfProgress, + int32_t aCurTotalProgress, + int32_t aMaxTotalProgress) { + if (!mListener) return NS_OK; + + // + // limit frequency of calls to OnProgressChange + // + + mCurProgress = (int64_t)aCurTotalProgress; + mMaxProgress = (int64_t)aMaxTotalProgress; + + if (mDelayedProgress) return NS_OK; + + if (!mDelayedStatus) { + MaybeSendProgress(); + StartDelayTimer(); + } + + mDelayedProgress = true; + + return NS_OK; +} + +NS_IMETHODIMP +nsBrowserStatusFilter::OnLocationChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, nsIURI* aLocation, + uint32_t aFlags) { + if (!mListener) return NS_OK; + + return mListener->OnLocationChange(aWebProgress, aRequest, aLocation, aFlags); +} + +NS_IMETHODIMP +nsBrowserStatusFilter::OnStatusChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, nsresult aStatus, + const char16_t* aMessage) { + if (!mListener) return NS_OK; + + // + // limit frequency of calls to OnStatusChange + // + if (mStatusIsDirty || !mCurrentStatusMsg.Equals(aMessage)) { + mStatusIsDirty = true; + mStatusMsg = aMessage; + } + + if (mDelayedStatus) return NS_OK; + + if (!mDelayedProgress) { + MaybeSendStatus(); + StartDelayTimer(); + } + + mDelayedStatus = true; + + return NS_OK; +} + +NS_IMETHODIMP +nsBrowserStatusFilter::OnSecurityChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, uint32_t aState) { + if (!mListener) return NS_OK; + + return mListener->OnSecurityChange(aWebProgress, aRequest, aState); +} + +NS_IMETHODIMP +nsBrowserStatusFilter::OnContentBlockingEvent(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + uint32_t aEvent) { + if (!mListener) return NS_OK; + + return mListener->OnContentBlockingEvent(aWebProgress, aRequest, aEvent); +} + +//----------------------------------------------------------------------------- +// nsBrowserStatusFilter::nsIWebProgressListener2 +//----------------------------------------------------------------------------- +NS_IMETHODIMP +nsBrowserStatusFilter::OnProgressChange64(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + int64_t aCurSelfProgress, + int64_t aMaxSelfProgress, + int64_t aCurTotalProgress, + int64_t aMaxTotalProgress) { + // XXX truncates 64-bit to 32-bit + return OnProgressChange(aWebProgress, aRequest, (int32_t)aCurSelfProgress, + (int32_t)aMaxSelfProgress, (int32_t)aCurTotalProgress, + (int32_t)aMaxTotalProgress); +} + +NS_IMETHODIMP +nsBrowserStatusFilter::OnRefreshAttempted(nsIWebProgress* aWebProgress, + nsIURI* aUri, uint32_t aDelay, + bool aSameUri, bool* allowRefresh) { + nsCOMPtr<nsIWebProgressListener2> listener = do_QueryInterface(mListener); + if (!listener) { + *allowRefresh = true; + return NS_OK; + } + + return listener->OnRefreshAttempted(aWebProgress, aUri, aDelay, aSameUri, + allowRefresh); +} + +//----------------------------------------------------------------------------- +// nsBrowserStatusFilter <private> +//----------------------------------------------------------------------------- + +void nsBrowserStatusFilter::ResetMembers() { + mMaxProgress = 0; + mCurProgress = 0; + mCurrentPercentage = 0; + mStatusIsDirty = true; + // We don't reset mIsLoadingDocument here. + // It's controlled by OnStateChange based on webProgress states. +} + +void nsBrowserStatusFilter::MaybeSendProgress() { + if (mCurProgress > mMaxProgress || mCurProgress <= 0) return; + + // check our percentage + int32_t percentage = (int32_t) double(mCurProgress) * 100 / mMaxProgress; + + // The progress meter only updates for increases greater than 3 percent + if (percentage > (mCurrentPercentage + 3)) { + mCurrentPercentage = percentage; + // XXX truncates 64-bit to 32-bit + mListener->OnProgressChange(nullptr, nullptr, 0, 0, (int32_t)mCurProgress, + (int32_t)mMaxProgress); + } +} + +void nsBrowserStatusFilter::MaybeSendStatus() { + if (mStatusIsDirty) { + mListener->OnStatusChange(nullptr, nullptr, NS_OK, mStatusMsg.get()); + mCurrentStatusMsg = mStatusMsg; + mStatusIsDirty = false; + } +} + +nsresult nsBrowserStatusFilter::StartDelayTimer() { + NS_ASSERTION(!DelayInEffect(), "delay should not be in effect"); + + return NS_NewTimerWithFuncCallback(getter_AddRefs(mTimer), TimeoutHandler, + this, 160, nsITimer::TYPE_ONE_SHOT, + "nsBrowserStatusFilter::TimeoutHandler", + mTarget); +} + +void nsBrowserStatusFilter::CallDelayedProgressListeners() { + mTimer = nullptr; + + if (!mListener) return; + + if (mDelayedStatus) { + mDelayedStatus = false; + MaybeSendStatus(); + } + + if (mDelayedProgress) { + mDelayedProgress = false; + MaybeSendProgress(); + } +} + +void nsBrowserStatusFilter::TimeoutHandler(nsITimer* aTimer, void* aClosure) { + nsBrowserStatusFilter* self = + reinterpret_cast<nsBrowserStatusFilter*>(aClosure); + if (!self) { + NS_ERROR("no self"); + return; + } + + self->CallDelayedProgressListeners(); +} + +NS_IMETHODIMP +nsBrowserStatusFilter::GetDocumentRequest(nsIRequest** aRequest) { + *aRequest = nullptr; + return NS_ERROR_NOT_IMPLEMENTED; +} diff --git a/toolkit/components/statusfilter/nsBrowserStatusFilter.h b/toolkit/components/statusfilter/nsBrowserStatusFilter.h new file mode 100644 index 0000000000..b61aecaabd --- /dev/null +++ b/toolkit/components/statusfilter/nsBrowserStatusFilter.h @@ -0,0 +1,78 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsBrowserStatusFilter_h__ +#define nsBrowserStatusFilter_h__ + +#include "nsIWebProgressListener.h" +#include "nsIWebProgressListener2.h" +#include "nsIWebProgress.h" +#include "nsWeakReference.h" +#include "nsCycleCollectionParticipant.h" +#include "nsITimer.h" +#include "nsCOMPtr.h" +#include "nsString.h" + +//----------------------------------------------------------------------------- +// nsBrowserStatusFilter - a web progress listener implementation designed to +// sit between nsDocLoader and nsBrowserStatusHandler to filter out and limit +// the frequency of certain events to improve page load performance. +//----------------------------------------------------------------------------- + +class nsBrowserStatusFilter : public nsIWebProgress, + public nsIWebProgressListener2, + public nsSupportsWeakReference { + public: + nsBrowserStatusFilter(); + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsBrowserStatusFilter, + nsIWebProgress) + NS_DECL_NSIWEBPROGRESS + NS_DECL_NSIWEBPROGRESSLISTENER + NS_DECL_NSIWEBPROGRESSLISTENER2 + + protected: + virtual ~nsBrowserStatusFilter(); + + private: + nsresult StartDelayTimer(); + void CallDelayedProgressListeners(); + void MaybeSendProgress(); + void MaybeSendStatus(); + void ResetMembers(); + bool DelayInEffect() { return mDelayedStatus || mDelayedProgress; } + + static void TimeoutHandler(nsITimer* aTimer, void* aClosure); + + private: + nsCOMPtr<nsIWebProgressListener> mListener; + nsCOMPtr<nsIEventTarget> mTarget; + nsCOMPtr<nsITimer> mTimer; + + // delayed values + nsString mStatusMsg; + int64_t mCurProgress; + int64_t mMaxProgress; + + nsString mCurrentStatusMsg; + int32_t mCurrentPercentage; + bool mStatusIsDirty; + bool mIsLoadingDocument; + + // indicates whether a timeout is pending + bool mDelayedStatus; + bool mDelayedProgress; +}; + +#define NS_BROWSERSTATUSFILTER_CONTRACTID \ + "@mozilla.org/appshell/component/browser-status-filter;1" +#define NS_BROWSERSTATUSFILTER_CID \ + { /* 6356aa16-7916-4215-a825-cbc2692ca87a */ \ + 0x6356aa16, 0x7916, 0x4215, { \ + 0xa8, 0x25, 0xcb, 0xc2, 0x69, 0x2c, 0xa8, 0x7a \ + } \ + } + +#endif // !nsBrowserStatusFilter_h__ |