/* 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 "BrowsingContextWebProgress.h" namespace mozilla { namespace dom { NS_IMPL_ADDREF(BrowsingContextWebProgress) NS_IMPL_RELEASE(BrowsingContextWebProgress) NS_INTERFACE_MAP_BEGIN(BrowsingContextWebProgress) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebProgress) NS_INTERFACE_MAP_ENTRY(nsIWebProgress) NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener) NS_INTERFACE_MAP_END NS_IMETHODIMP BrowsingContextWebProgress::AddProgressListener( nsIWebProgressListener* aListener, uint32_t aNotifyMask) { nsWeakPtr listener = do_GetWeakReference(aListener); if (!listener) { return NS_ERROR_INVALID_ARG; } if (mListenerInfoList.Contains(listener)) { // The listener is already registered! return NS_ERROR_FAILURE; } mListenerInfoList.AppendElement(ListenerInfo(listener, aNotifyMask)); return NS_OK; } NS_IMETHODIMP BrowsingContextWebProgress::RemoveProgressListener( nsIWebProgressListener* aListener) { nsWeakPtr listener = do_GetWeakReference(aListener); if (!listener) { return NS_ERROR_INVALID_ARG; } return mListenerInfoList.RemoveElement(listener) ? NS_OK : NS_ERROR_FAILURE; } NS_IMETHODIMP BrowsingContextWebProgress::GetDOMWindow( mozIDOMWindowProxy** aDOMWindow) { return NS_ERROR_NOT_AVAILABLE; } NS_IMETHODIMP BrowsingContextWebProgress::GetIsTopLevel(bool* aIsTopLevel) { *aIsTopLevel = true; return NS_OK; } NS_IMETHODIMP BrowsingContextWebProgress::GetIsLoadingDocument( bool* aIsLoadingDocument) { *aIsLoadingDocument = false; return NS_OK; } NS_IMETHODIMP BrowsingContextWebProgress::GetLoadType(uint32_t* aLoadType) { *aLoadType = 0; return NS_OK; } NS_IMETHODIMP BrowsingContextWebProgress::GetTarget(nsIEventTarget** aTarget) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP BrowsingContextWebProgress::SetTarget(nsIEventTarget* aTarget) { return NS_ERROR_NOT_IMPLEMENTED; } void BrowsingContextWebProgress::UpdateAndNotifyListeners( uint32_t aFlag, const std::function& aCallback) { RefPtr kungFuDeathGrip = this; ListenerArray::ForwardIterator iter(mListenerInfoList); while (iter.HasMore()) { ListenerInfo& info = iter.GetNext(); if (!(info.mNotifyMask & aFlag)) { continue; } nsCOMPtr listener = do_QueryReferent(info.mWeakListener); if (!listener) { mListenerInfoList.RemoveElement(info); continue; } aCallback(listener); } mListenerInfoList.Compact(); } //////////////////////////////////////////////////////////////////////////////// // nsIWebProgressListener NS_IMETHODIMP BrowsingContextWebProgress::OnStateChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, uint32_t aStateFlags, nsresult aStatus) { const uint32_t startDocumentFlags = nsIWebProgressListener::STATE_START | nsIWebProgressListener::STATE_IS_DOCUMENT | nsIWebProgressListener::STATE_IS_REQUEST | nsIWebProgressListener::STATE_IS_WINDOW | nsIWebProgressListener::STATE_IS_NETWORK; bool isTopLevel = false; nsresult rv = aWebProgress->GetIsTopLevel(&isTopLevel); NS_ENSURE_SUCCESS(rv, rv); bool isTopLevelStartDocumentEvent = (aStateFlags & startDocumentFlags) == startDocumentFlags && isTopLevel; // If we receive a matching STATE_START for a top-level document event, // and we are currently not suspending this event, start suspending all // further matching STATE_START events after this one. if (isTopLevelStartDocumentEvent && !mSuspendOnStateStartChangeEvents) { mSuspendOnStateStartChangeEvents = true; } else if (mSuspendOnStateStartChangeEvents) { // If we are currently suspending matching STATE_START events, check if this // is a corresponding STATE_STOP event. const uint32_t stopWindowFlags = nsIWebProgressListener::STATE_STOP | nsIWebProgressListener::STATE_IS_WINDOW; bool isTopLevelStopWindowEvent = (aStateFlags & stopWindowFlags) == stopWindowFlags && isTopLevel; if (isTopLevelStopWindowEvent) { // If this is a STATE_STOP event corresponding to the initial STATE_START // event, stop suspending matching STATE_START events. mSuspendOnStateStartChangeEvents = false; } else if (isTopLevelStartDocumentEvent) { // We have received a matching STATE_START event at least twice, but // haven't received the corresponding STATE_STOP event for the first one. // Don't let this event through. This is probably a duplicate event from // the new BrowserParent due to a process switch. return NS_OK; } // Allow all other progress events that don't match top-level start document // flags. } UpdateAndNotifyListeners( ((aStateFlags >> 16) & nsIWebProgress::NOTIFY_STATE_ALL), [&](nsIWebProgressListener* listener) { listener->OnStateChange(aWebProgress, aRequest, aStateFlags, aStatus); }); return NS_OK; } NS_IMETHODIMP BrowsingContextWebProgress::OnProgressChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, int32_t aCurSelfProgress, int32_t aMaxSelfProgress, int32_t aCurTotalProgress, int32_t aMaxTotalProgress) { UpdateAndNotifyListeners( nsIWebProgress::NOTIFY_PROGRESS, [&](nsIWebProgressListener* listener) { listener->OnProgressChange(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress); }); return NS_OK; } NS_IMETHODIMP BrowsingContextWebProgress::OnLocationChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, nsIURI* aLocation, uint32_t aFlags) { UpdateAndNotifyListeners( nsIWebProgress::NOTIFY_LOCATION, [&](nsIWebProgressListener* listener) { listener->OnLocationChange(aWebProgress, aRequest, aLocation, aFlags); }); return NS_OK; } NS_IMETHODIMP BrowsingContextWebProgress::OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, nsresult aStatus, const char16_t* aMessage) { UpdateAndNotifyListeners( nsIWebProgress::NOTIFY_STATUS, [&](nsIWebProgressListener* listener) { listener->OnStatusChange(aWebProgress, aRequest, aStatus, aMessage); }); return NS_OK; } NS_IMETHODIMP BrowsingContextWebProgress::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, uint32_t aState) { UpdateAndNotifyListeners( nsIWebProgress::NOTIFY_SECURITY, [&](nsIWebProgressListener* listener) { listener->OnSecurityChange(aWebProgress, aRequest, aState); }); return NS_OK; } NS_IMETHODIMP BrowsingContextWebProgress::OnContentBlockingEvent(nsIWebProgress* aWebProgress, nsIRequest* aRequest, uint32_t aEvent) { UpdateAndNotifyListeners(nsIWebProgress::NOTIFY_CONTENT_BLOCKING, [&](nsIWebProgressListener* listener) { listener->OnContentBlockingEvent(aWebProgress, aRequest, aEvent); }); return NS_OK; } } // namespace dom } // namespace mozilla