1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef nsDocLoader_h__
7 #define nsDocLoader_h__
9 #include "nsIDocumentLoader.h"
10 #include "nsIWebProgress.h"
11 #include "nsIWebProgressListener.h"
12 #include "nsIRequestObserver.h"
13 #include "nsWeakReference.h"
14 #include "nsILoadGroup.h"
15 #include "nsCOMArray.h"
16 #include "nsTObserverArray.h"
18 #include "nsIChannel.h"
19 #include "nsIProgressEventSink.h"
20 #include "nsIInterfaceRequestor.h"
21 #include "nsIInterfaceRequestorUtils.h"
22 #include "nsIChannelEventSink.h"
23 #include "nsISupportsPriority.h"
25 #include "PLDHashTable.h"
26 #include "nsCycleCollectionParticipant.h"
28 #include "mozilla/LinkedList.h"
29 #include "mozilla/UniquePtr.h"
33 class BrowsingContext
;
35 } // namespace mozilla
37 /****************************************************************************
38 * nsDocLoader implementation...
39 ****************************************************************************/
41 #define NS_THIS_DOCLOADER_IMPL_CID \
42 { /* b4ec8387-98aa-4c08-93b6-6d23069c06f2 */ \
43 0xb4ec8387, 0x98aa, 0x4c08, { \
44 0x93, 0xb6, 0x6d, 0x23, 0x06, 0x9c, 0x06, 0xf2 \
48 class nsDocLoader
: public nsIDocumentLoader
,
49 public nsIRequestObserver
,
50 public nsSupportsWeakReference
,
51 public nsIProgressEventSink
,
52 public nsIWebProgress
,
53 public nsIInterfaceRequestor
,
54 public nsIChannelEventSink
,
55 public nsISupportsPriority
{
57 NS_DECLARE_STATIC_IID_ACCESSOR(NS_THIS_DOCLOADER_IMPL_CID
)
59 nsDocLoader() : nsDocLoader(false) {}
61 [[nodiscard
]] virtual nsresult
Init();
62 [[nodiscard
]] nsresult
InitWithBrowsingContext(
63 mozilla::dom::BrowsingContext
* aBrowsingContext
);
65 static already_AddRefed
<nsDocLoader
> GetAsDocLoader(nsISupports
* aSupports
);
66 // Needed to deal with ambiguous inheritance from nsISupports...
67 static nsISupports
* GetAsSupports(nsDocLoader
* aDocLoader
) {
68 return static_cast<nsIDocumentLoader
*>(aDocLoader
);
71 // Add aDocLoader as a child to the docloader service.
72 [[nodiscard
]] static nsresult
AddDocLoaderAsChildOfRoot(
73 nsDocLoader
* aDocLoader
);
75 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
76 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDocLoader
, nsIDocumentLoader
)
78 NS_DECL_NSIDOCUMENTLOADER
80 // nsIProgressEventSink
81 NS_DECL_NSIPROGRESSEVENTSINK
83 // nsIRequestObserver methods: (for observing the load group)
84 NS_DECL_NSIREQUESTOBSERVER
85 NS_DECL_NSIWEBPROGRESS
87 NS_DECL_NSIINTERFACEREQUESTOR
88 NS_DECL_NSICHANNELEVENTSINK
89 NS_DECL_NSISUPPORTSPRIORITY
; // semicolon for clang-format bug 1629756
91 // Implementation specific methods...
93 // Remove aChild from our childlist. This nulls out the child's mParent
95 [[nodiscard
]] nsresult
RemoveChildLoader(nsDocLoader
* aChild
);
97 // Add aChild to our child list. This will set aChild's mParent pointer to
99 [[nodiscard
]] nsresult
AddChildLoader(nsDocLoader
* aChild
);
100 nsDocLoader
* GetParent() const { return mParent
; }
102 struct nsListenerInfo
{
103 nsListenerInfo(nsIWeakReference
* aListener
, unsigned long aNotifyMask
)
104 : mWeakListener(aListener
), mNotifyMask(aNotifyMask
) {}
106 // Weak pointer for the nsIWebProgressListener...
107 nsWeakPtr mWeakListener
;
109 // Mask indicating which notifications the listener wants to receive.
110 unsigned long mNotifyMask
;
114 * Fired when a security change occurs due to page transitions,
115 * or end document load. This interface should be called by
116 * a security package (eg Netscape Personal Security Manager)
117 * to notify nsIWebProgressListeners that security state has
118 * changed. State flags are in nsIWebProgressListener.idl
120 void OnSecurityChange(nsISupports
* aContext
, uint32_t aState
);
122 void SetDocumentOpenedButNotLoaded() { mDocumentOpenedButNotLoaded
= true; }
124 uint32_t ChildCount() const { return mChildList
.Length(); }
126 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
127 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void OOPChildrenLoadingIsEmpty() {
128 DocLoaderIsEmpty(true);
132 explicit nsDocLoader(bool aNotifyAboutBackgroundRequests
);
133 virtual ~nsDocLoader();
135 [[nodiscard
]] virtual nsresult
SetDocLoaderParent(nsDocLoader
* aLoader
);
139 void SetBackgroundLoadIframe();
142 virtual void DestroyChildren();
144 nsIDocumentLoader
* ChildAt(int32_t i
) {
145 return mChildList
.SafeElementAt(i
, nullptr);
148 void FireOnProgressChange(nsDocLoader
* aLoadInitiator
, nsIRequest
* request
,
149 int64_t aProgress
, int64_t aProgressMax
,
150 int64_t aProgressDelta
, int64_t aTotalProgress
,
151 int64_t aMaxTotalProgress
);
153 // This should be at least 2 long since we'll generally always
154 // have the current page and the global docloader on the ancestor
155 // list. But to deal with frames it's better to make it a bit
156 // longer, and it's always a stack temporary so there's no real
158 typedef AutoTArray
<RefPtr
<nsDocLoader
>, 8> WebProgressList
;
159 void GatherAncestorWebProgresses(WebProgressList
& aList
);
161 void FireOnStateChange(nsIWebProgress
* aProgress
, nsIRequest
* request
,
162 int32_t aStateFlags
, nsresult aStatus
);
164 // The guts of FireOnStateChange, but does not call itself on our ancestors.
165 // The arguments that are const are const so that we can detect cases when
166 // DoFireOnStateChange wants to propagate changes to the next web progress
167 // at compile time. The ones that are not, are references so that such
168 // changes can be propagated.
169 void DoFireOnStateChange(nsIWebProgress
* const aProgress
,
170 nsIRequest
* const request
, int32_t& aStateFlags
,
171 const nsresult aStatus
);
173 void FireOnStatusChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
174 nsresult aStatus
, const char16_t
* aMessage
);
176 void FireOnLocationChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
177 nsIURI
* aUri
, uint32_t aFlags
);
179 [[nodiscard
]] bool RefreshAttempted(nsIWebProgress
* aWebProgress
,
180 nsIURI
* aURI
, uint32_t aDelay
,
183 // this function is overridden by the docshell, it is provided so that we
184 // can pass more information about redirect state (the normal OnStateChange
185 // doesn't get the new channel).
186 // @param aRedirectFlags The flags being sent to OnStateChange that
187 // indicate the type of redirect.
188 // @param aStateFlags The channel flags normally sent to OnStateChange.
189 virtual void OnRedirectStateChange(nsIChannel
* aOldChannel
,
190 nsIChannel
* aNewChannel
,
191 uint32_t aRedirectFlags
,
192 uint32_t aStateFlags
) {}
194 void doStartDocumentLoad();
195 void doStartURLLoad(nsIRequest
* request
, int32_t aExtraFlags
);
196 void doStopURLLoad(nsIRequest
* request
, nsresult aStatus
);
197 void doStopDocumentLoad(nsIRequest
* request
, nsresult aStatus
);
199 void NotifyDoneWithOnload(nsDocLoader
* aParent
);
201 // Inform a parent docloader that aChild is about to call its onload
203 [[nodiscard
]] bool ChildEnteringOnload(nsIDocumentLoader
* aChild
) {
204 // It's ok if we're already in the list -- we'll just be in there twice
205 // and then the RemoveObject calls from ChildDoneWithOnload will remove
207 return mChildrenInOnload
.AppendObject(aChild
);
210 // Inform a parent docloader that aChild is done calling its onload
212 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
213 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void ChildDoneWithOnload(
214 nsIDocumentLoader
* aChild
) {
215 mChildrenInOnload
.RemoveObject(aChild
);
216 DocLoaderIsEmpty(true);
219 // DocLoaderIsEmpty should be called whenever the docloader may be empty.
220 // This method is idempotent and does nothing if the docloader is not in
221 // fact empty. This method _does_ make sure that layout is flushed if our
222 // loadgroup has no active requests before checking for "real" emptiness if
223 // aFlushLayout is true.
224 // @param aOverrideStatus An optional status to use when notifying listeners
225 // of the completed load, instead of using the load group's status.
226 MOZ_CAN_RUN_SCRIPT
void DocLoaderIsEmpty(
228 const mozilla::Maybe
<nsresult
>& aOverrideStatus
= mozilla::Nothing());
231 struct nsStatusInfo
: public mozilla::LinkedListElement
<nsStatusInfo
> {
232 nsString mStatusMessage
;
233 nsresult mStatusCode
;
234 // Weak mRequest is ok; we'll be told if it decides to go away.
235 nsIRequest
* const mRequest
;
237 explicit nsStatusInfo(nsIRequest
* aRequest
)
238 : mStatusCode(NS_ERROR_NOT_INITIALIZED
), mRequest(aRequest
) {
239 MOZ_COUNT_CTOR(nsStatusInfo
);
241 MOZ_COUNTED_DTOR(nsStatusInfo
)
244 struct nsRequestInfo
: public PLDHashEntryHdr
{
245 explicit nsRequestInfo(const void* key
)
250 mLastStatus(nullptr) {
251 MOZ_COUNT_CTOR(nsRequestInfo
);
254 MOZ_COUNTED_DTOR(nsRequestInfo
)
256 nsIRequest
* Request() {
257 return static_cast<nsIRequest
*>(const_cast<void*>(mKey
));
260 const void* mKey
; // Must be first for the PLDHashTable stubs to work
261 int64_t mCurrentProgress
;
262 int64_t mMaxProgress
;
265 mozilla::UniquePtr
<nsStatusInfo
> mLastStatus
;
268 static void RequestInfoHashInitEntry(PLDHashEntryHdr
* entry
, const void* key
);
269 static void RequestInfoHashClearEntry(PLDHashTable
* table
,
270 PLDHashEntryHdr
* entry
);
272 // IMPORTANT: The ownership implicit in the following member
273 // variables has been explicitly checked and set using nsCOMPtr
274 // for owning pointers and raw COM interface pointers for weak
275 // (ie, non owning) references. If you add any members to this
276 // class, please make the ownership explicit (pinkerton, scc).
278 nsCOMPtr
<nsIRequest
> mDocumentRequest
; // [OWNER] ???compare with document
280 nsDocLoader
* mParent
; // [WEAK]
282 typedef nsAutoTObserverArray
<nsListenerInfo
, 8> ListenerArray
;
283 ListenerArray mListenerInfoList
;
285 nsCOMPtr
<nsILoadGroup
> mLoadGroup
;
286 // We hold weak refs to all our kids
287 nsTObserverArray
<nsDocLoader
*> mChildList
;
289 // The following member variables are related to the new nsIWebProgress
290 // feedback interfaces that travis cooked up.
291 int32_t mProgressStateFlags
;
293 int64_t mCurrentSelfProgress
;
294 int64_t mMaxSelfProgress
;
296 int64_t mCurrentTotalProgress
;
297 int64_t mMaxTotalProgress
;
299 PLDHashTable mRequestInfoHash
;
300 int64_t mCompletedTotalProgress
;
302 mozilla::LinkedList
<nsStatusInfo
> mStatusInfoList
;
305 * This flag indicates that the loader is loading a document. It is set
306 * from the call to LoadDocument(...) until the OnConnectionsComplete(...)
307 * notification is fired...
309 bool mIsLoadingDocument
;
311 /* Flag to indicate that we're in the process of restoring a document. */
312 bool mIsRestoringDocument
;
314 /* Flag to indicate that we're in the process of flushing layout
315 under DocLoaderIsEmpty() and should not do another flush. */
316 bool mDontFlushLayout
;
318 /* Flag to indicate whether we should consider ourselves as currently
319 flushing layout for the purposes of IsBusy. For example, if Stop has
320 been called then IsBusy should return false even if we are still
322 bool mIsFlushingLayout
;
326 * This flag indicates that the loader is waiting for completion of
327 * a document.open-triggered "document load". This is set when
328 * document.open() happens and sets up a new parser and cleared out
329 * when we go to fire our load event or end up with a new document
332 bool mDocumentOpenedButNotLoaded
;
334 bool mNotifyAboutBackgroundRequests
;
336 static const PLDHashTableOps sRequestInfoHashOps
;
338 // A list of kids that are in the middle of their onload calls and will let
339 // us know once they're done. We don't want to fire onload for "normal"
340 // DocLoaderIsEmpty calls (those coming from requests finishing in our
341 // loadgroup) unless this is empty.
342 nsCOMArray
<nsIDocumentLoader
> mChildrenInOnload
;
344 int64_t GetMaxTotalProgress();
346 nsresult
AddRequestInfo(nsIRequest
* aRequest
);
347 void RemoveRequestInfo(nsIRequest
* aRequest
);
348 nsRequestInfo
* GetRequestInfo(nsIRequest
* aRequest
) const;
349 void ClearRequestInfoHash();
350 int64_t CalculateMaxProgress();
351 /// void DumpChannelInfo(void);
353 // used to clear our internal progress state between loads...
354 void ClearInternalProgress();
357 * Used to test whether we might need to fire a load event. This
358 * can happen when we have a document load going on, or when we've
359 * had document.open() called and haven't fired the corresponding
362 bool IsBlockingLoadEvent() const {
363 return mIsLoadingDocument
|| mDocumentOpenedButNotLoaded
;
367 NS_DEFINE_STATIC_IID_ACCESSOR(nsDocLoader
, NS_THIS_DOCLOADER_IMPL_CID
)
369 static inline nsISupports
* ToSupports(nsDocLoader
* aDocLoader
) {
370 return static_cast<nsIDocumentLoader
*>(aDocLoader
);
373 #endif /* nsDocLoader_h__ */