1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef mozilla_dom_SessionHistoryEntry_h
8 #define mozilla_dom_SessionHistoryEntry_h
9 
10 #include "mozilla/Maybe.h"
11 #include "mozilla/UniquePtr.h"
12 #include "nsILayoutHistoryState.h"
13 #include "nsISHEntry.h"
14 #include "nsSHEntryShared.h"
15 #include "nsStructuredCloneContainer.h"
16 #include "nsTHashMap.h"
17 
18 class nsDocShellLoadState;
19 class nsIChannel;
20 class nsIInputStream;
21 class nsIReferrerInfo;
22 class nsISHistory;
23 class nsIURI;
24 
25 namespace mozilla::ipc {
26 template <typename P>
27 struct IPDLParamTraits;
28 }
29 
30 namespace mozilla {
31 namespace dom {
32 
33 struct LoadingSessionHistoryInfo;
34 class SessionHistoryEntry;
35 class SHEntrySharedParentState;
36 
37 // SessionHistoryInfo stores session history data for a load. It can be sent
38 // over IPC and is used in both the parent and the child processes.
39 class SessionHistoryInfo {
40  public:
41   SessionHistoryInfo() = default;
42   SessionHistoryInfo(const SessionHistoryInfo& aInfo) = default;
43   SessionHistoryInfo(nsDocShellLoadState* aLoadState, nsIChannel* aChannel);
44   SessionHistoryInfo(const SessionHistoryInfo& aSharedStateFrom, nsIURI* aURI);
45   SessionHistoryInfo(nsIURI* aURI, nsIPrincipal* aTriggeringPrincipal,
46                      nsIPrincipal* aPrincipalToInherit,
47                      nsIPrincipal* aPartitionedPrincipalToInherit,
48                      nsIContentSecurityPolicy* aCsp,
49                      const nsACString& aContentType);
50   SessionHistoryInfo(nsIChannel* aChannel, uint32_t aLoadType,
51                      nsIPrincipal* aPartitionedPrincipalToInherit,
52                      nsIContentSecurityPolicy* aCsp);
53 
54   void Reset(nsIURI* aURI, const nsID& aDocShellID, bool aDynamicCreation,
55              nsIPrincipal* aTriggeringPrincipal,
56              nsIPrincipal* aPrincipalToInherit,
57              nsIPrincipal* aPartitionedPrincipalToInherit,
58              nsIContentSecurityPolicy* aCsp, const nsACString& aContentType);
59 
60   bool operator==(const SessionHistoryInfo& aInfo) const {
61     return false;  // FIXME
62   }
63 
GetURI()64   nsIURI* GetURI() const { return mURI; }
SetURI(nsIURI * aURI)65   void SetURI(nsIURI* aURI) { mURI = aURI; }
66 
SetOriginalURI(nsIURI * aOriginalURI)67   void SetOriginalURI(nsIURI* aOriginalURI) { mOriginalURI = aOriginalURI; }
68 
SetResultPrincipalURI(nsIURI * aResultPrincipalURI)69   void SetResultPrincipalURI(nsIURI* aResultPrincipalURI) {
70     mResultPrincipalURI = aResultPrincipalURI;
71   }
72 
GetPostData()73   nsIInputStream* GetPostData() const { return mPostData; }
SetPostData(nsIInputStream * aPostData)74   void SetPostData(nsIInputStream* aPostData) { mPostData = aPostData; }
75 
GetScrollPosition(int32_t * aScrollPositionX,int32_t * aScrollPositionY)76   void GetScrollPosition(int32_t* aScrollPositionX, int32_t* aScrollPositionY) {
77     *aScrollPositionX = mScrollPositionX;
78     *aScrollPositionY = mScrollPositionY;
79   }
80 
SetScrollPosition(int32_t aScrollPositionX,int32_t aScrollPositionY)81   void SetScrollPosition(int32_t aScrollPositionX, int32_t aScrollPositionY) {
82     mScrollPositionX = aScrollPositionX;
83     mScrollPositionY = aScrollPositionY;
84   }
85 
GetScrollRestorationIsManual()86   bool GetScrollRestorationIsManual() const {
87     return mScrollRestorationIsManual;
88   }
GetTitle()89   const nsAString& GetTitle() { return mTitle; }
SetTitle(const nsAString & aTitle)90   void SetTitle(const nsAString& aTitle) {
91     mTitle = aTitle;
92     MaybeUpdateTitleFromURI();
93   }
94 
GetName()95   const nsAString& GetName() { return mName; }
SetName(const nsAString & aName)96   void SetName(const nsAString& aName) { mName = aName; }
97 
SetScrollRestorationIsManual(bool aIsManual)98   void SetScrollRestorationIsManual(bool aIsManual) {
99     mScrollRestorationIsManual = aIsManual;
100   }
101 
GetStateData()102   nsStructuredCloneContainer* GetStateData() const { return mStateData; }
SetStateData(nsStructuredCloneContainer * aStateData)103   void SetStateData(nsStructuredCloneContainer* aStateData) {
104     mStateData = aStateData;
105   }
106 
SetLoadReplace(bool aLoadReplace)107   void SetLoadReplace(bool aLoadReplace) { mLoadReplace = aLoadReplace; }
108 
SetURIWasModified(bool aURIWasModified)109   void SetURIWasModified(bool aURIWasModified) {
110     mURIWasModified = aURIWasModified;
111   }
GetURIWasModified()112   bool GetURIWasModified() const { return mURIWasModified; }
113 
SetHasUserInteraction(bool aHasUserInteraction)114   void SetHasUserInteraction(bool aHasUserInteraction) {
115     mHasUserInteraction = aHasUserInteraction;
116   }
GetHasUserInteraction()117   bool GetHasUserInteraction() const { return mHasUserInteraction; }
118 
119   uint64_t SharedId() const;
120 
121   nsILayoutHistoryState* GetLayoutHistoryState();
122   void SetLayoutHistoryState(nsILayoutHistoryState* aState);
123 
124   nsIPrincipal* GetTriggeringPrincipal() const;
125 
126   nsIPrincipal* GetPrincipalToInherit() const;
127 
128   nsIPrincipal* GetPartitionedPrincipalToInherit() const;
129 
130   nsIContentSecurityPolicy* GetCsp() const;
131 
132   uint32_t GetCacheKey() const;
133   void SetCacheKey(uint32_t aCacheKey);
134 
135   bool IsSubFrame() const;
136 
SharesDocumentWith(const SessionHistoryInfo & aOther)137   bool SharesDocumentWith(const SessionHistoryInfo& aOther) const {
138     return SharedId() == aOther.SharedId();
139   }
140 
141   void FillLoadInfo(nsDocShellLoadState& aLoadState) const;
142 
LoadType()143   uint32_t LoadType() { return mLoadType; }
144 
145   void SetSaveLayoutStateFlag(bool aSaveLayoutStateFlag);
146 
147  private:
148   friend class SessionHistoryEntry;
149   friend struct mozilla::ipc::IPDLParamTraits<SessionHistoryInfo>;
150 
151   void MaybeUpdateTitleFromURI();
152 
153   nsCOMPtr<nsIURI> mURI;
154   nsCOMPtr<nsIURI> mOriginalURI;
155   nsCOMPtr<nsIURI> mResultPrincipalURI;
156   nsCOMPtr<nsIReferrerInfo> mReferrerInfo;
157   nsString mTitle;
158   nsString mName;
159   nsCOMPtr<nsIInputStream> mPostData;
160   uint32_t mLoadType = 0;
161   int32_t mScrollPositionX = 0;
162   int32_t mScrollPositionY = 0;
163   RefPtr<nsStructuredCloneContainer> mStateData;
164   Maybe<nsString> mSrcdocData;
165   nsCOMPtr<nsIURI> mBaseURI;
166 
167   bool mLoadReplace = false;
168   bool mURIWasModified = false;
169   bool mScrollRestorationIsManual = false;
170   bool mPersist = true;
171   bool mHasUserInteraction = false;
172   bool mHasUserActivation = false;
173 
174   union SharedState {
175     SharedState();
176     explicit SharedState(const SharedState& aOther);
177     explicit SharedState(const Maybe<const SharedState&>& aOther);
178     ~SharedState();
179 
180     SharedState& operator=(const SharedState& aOther);
181 
182     SHEntrySharedState* Get() const;
183 
184     void Set(SHEntrySharedParentState* aState) { mParent = aState; }
185 
186     void ChangeId(uint64_t aId);
187 
188     static SharedState Create(nsIPrincipal* aTriggeringPrincipal,
189                               nsIPrincipal* aPrincipalToInherit,
190                               nsIPrincipal* aPartitionedPrincipalToInherit,
191                               nsIContentSecurityPolicy* aCsp,
192                               const nsACString& aContentType);
193 
194    private:
195     explicit SharedState(SHEntrySharedParentState* aParent)
196         : mParent(aParent) {}
197     explicit SharedState(UniquePtr<SHEntrySharedState>&& aChild)
198         : mChild(std::move(aChild)) {}
199 
200     void Init();
201     void Init(const SharedState& aOther);
202 
203     // In the parent process this holds a strong reference to the refcounted
204     // SHEntrySharedParentState. In the child processes this holds an owning
205     // pointer to a SHEntrySharedState.
206     RefPtr<SHEntrySharedParentState> mParent;
207     UniquePtr<SHEntrySharedState> mChild;
208   };
209 
210   SharedState mSharedState;
211 };
212 
213 struct LoadingSessionHistoryInfo {
214   LoadingSessionHistoryInfo() = default;
215   explicit LoadingSessionHistoryInfo(SessionHistoryEntry* aEntry);
216   // Initializes mInfo using aEntry and otherwise copies the values from aInfo.
217   LoadingSessionHistoryInfo(SessionHistoryEntry* aEntry,
218                             LoadingSessionHistoryInfo* aInfo);
219 
220   // For about:blank only.
221   explicit LoadingSessionHistoryInfo(const SessionHistoryInfo& aInfo);
222 
223   already_AddRefed<nsDocShellLoadState> CreateLoadInfo() const;
224 
225   SessionHistoryInfo mInfo;
226 
227   uint64_t mLoadId = 0;
228 
229   // The following three member variables are used to inform about a load from
230   // the session history. The session-history-in-child approach has just
231   // an nsISHEntry in the nsDocShellLoadState and access to the nsISHistory,
232   // but session-history-in-parent needs to pass needed information explicitly
233   // to the relevant child process.
234   bool mLoadIsFromSessionHistory = false;
235   // mRequestedIndex, mSessionHistoryLength and mLoadingCurrentActiveEntry are
236   // relevant only if mLoadIsFromSessionHistory is true.
237   int32_t mRequestedIndex = -1;
238   int32_t mSessionHistoryLength = 0;
239   // If we're loading from the current active entry we want to treat it as not
240   // a same-document navigation (see nsDocShell::IsSameDocumentNavigation).
241   bool mLoadingCurrentActiveEntry = false;
242   // If mForceMaybeResetName.isSome() is true then the parent process has
243   // determined whether the BC's name should be cleared and stored in session
244   // history (see https://html.spec.whatwg.org/#history-traversal step 4.2).
245   // This is used when we're replacing the BC for BFCache in the parent. In
246   // other cases mForceMaybeResetName.isSome() will be false and the child
247   // process should be able to make that determination itself.
248   Maybe<bool> mForceMaybeResetName;
249 };
250 
251 // HistoryEntryCounterForBrowsingContext is used to count the number of entries
252 // which are added to the session history for a particular browsing context.
253 // If a SessionHistoryEntry is cloned because of navigation in some other
254 // browsing context, that doesn't cause the counter value to be increased.
255 // The browsing context specific counter is needed to make it easier to
256 // synchronously update history.length value in a child process when
257 // an iframe is removed from DOM.
258 class HistoryEntryCounterForBrowsingContext {
259  public:
260   HistoryEntryCounterForBrowsingContext()
261       : mCounter(new RefCountedCounter()), mHasModified(false) {
262     ++(*this);
263   }
264 
265   HistoryEntryCounterForBrowsingContext(
266       const HistoryEntryCounterForBrowsingContext& aOther)
267       : mCounter(aOther.mCounter), mHasModified(false) {}
268 
269   HistoryEntryCounterForBrowsingContext(
270       HistoryEntryCounterForBrowsingContext&& aOther) = delete;
271 
272   ~HistoryEntryCounterForBrowsingContext() {
273     if (mHasModified) {
274       --(*mCounter);
275     }
276   }
277 
278   void CopyValueFrom(const HistoryEntryCounterForBrowsingContext& aOther) {
279     if (mHasModified) {
280       --(*mCounter);
281     }
282     mCounter = aOther.mCounter;
283     mHasModified = false;
284   }
285 
286   HistoryEntryCounterForBrowsingContext& operator=(
287       const HistoryEntryCounterForBrowsingContext& aOther) = delete;
288 
289   HistoryEntryCounterForBrowsingContext& operator++() {
290     mHasModified = true;
291     ++(*mCounter);
292     return *this;
293   }
294 
295   operator uint32_t() const { return *mCounter; }
296 
297   bool Modified() { return mHasModified; }
298 
299   void SetModified(bool aModified) { mHasModified = aModified; }
300 
301   void Reset() {
302     if (mHasModified) {
303       --(*mCounter);
304     }
305     mCounter = new RefCountedCounter();
306     mHasModified = false;
307   }
308 
309  private:
310   class RefCountedCounter {
311    public:
312     NS_INLINE_DECL_REFCOUNTING(
313         mozilla::dom::HistoryEntryCounterForBrowsingContext::RefCountedCounter)
314 
315     RefCountedCounter& operator++() {
316       ++mCounter;
317       return *this;
318     }
319 
320     RefCountedCounter& operator--() {
321       --mCounter;
322       return *this;
323     }
324 
325     operator uint32_t() const { return mCounter; }
326 
327    private:
328     ~RefCountedCounter() = default;
329 
330     uint32_t mCounter = 0;
331   };
332 
333   RefPtr<RefCountedCounter> mCounter;
334   bool mHasModified;
335 };
336 
337 // SessionHistoryEntry is used to store session history data in the parent
338 // process. It holds a SessionHistoryInfo, some state shared amongst multiple
339 // SessionHistoryEntries, a parent and children.
340 #define NS_SESSIONHISTORYENTRY_IID                   \
341   {                                                  \
342     0x5b66a244, 0x8cec, 0x4caa, {                    \
343       0xaa, 0x0a, 0x78, 0x92, 0xfd, 0x17, 0xa6, 0x67 \
344     }                                                \
345   }
346 
347 class SessionHistoryEntry : public nsISHEntry {
348  public:
349   SessionHistoryEntry(nsDocShellLoadState* aLoadState, nsIChannel* aChannel);
350   SessionHistoryEntry();
351   explicit SessionHistoryEntry(SessionHistoryInfo* aInfo);
352   explicit SessionHistoryEntry(const SessionHistoryEntry& aEntry);
353 
354   NS_DECL_ISUPPORTS
355   NS_DECL_NSISHENTRY
356   NS_DECLARE_STATIC_IID_ACCESSOR(NS_SESSIONHISTORYENTRY_IID)
357 
358   void ReplaceWith(const SessionHistoryEntry& aSource);
359 
360   const SessionHistoryInfo& Info() const { return *mInfo; }
361 
362   SHEntrySharedParentState* SharedInfo() const;
363 
364   void SetFrameLoader(nsFrameLoader* aFrameLoader);
365   nsFrameLoader* GetFrameLoader();
366 
367   void AddChild(SessionHistoryEntry* aChild, int32_t aOffset,
368                 bool aUseRemoteSubframes);
369   void RemoveChild(SessionHistoryEntry* aChild);
370   // Finds the child with the same docshell ID as aNewChild, replaces it with
371   // aNewChild and returns true. If there is no child with the same docshell ID
372   // then it returns false.
373   bool ReplaceChild(SessionHistoryEntry* aNewChild);
374 
375   void SetInfo(SessionHistoryInfo* aInfo);
376 
377   bool ForInitialLoad() { return mForInitialLoad; }
378   void SetForInitialLoad(bool aForInitialLoad) {
379     mForInitialLoad = aForInitialLoad;
380   }
381 
382   const nsID& DocshellID() const;
383 
384   HistoryEntryCounterForBrowsingContext& BCHistoryLength() {
385     return mBCHistoryLength;
386   }
387 
388   void SetBCHistoryLength(HistoryEntryCounterForBrowsingContext& aCounter) {
389     mBCHistoryLength.CopyValueFrom(aCounter);
390   }
391 
392   void ClearBCHistoryLength() { mBCHistoryLength.Reset(); }
393 
394   void SetIsDynamicallyAdded(bool aDynamic);
395 
396   // Get an entry based on LoadingSessionHistoryInfo's mLoadId. Parent process
397   // only.
398   static SessionHistoryEntry* GetByLoadId(uint64_t aLoadId);
399   static void SetByLoadId(uint64_t aLoadId, SessionHistoryEntry* aEntry);
400   static void RemoveLoadId(uint64_t aLoadId);
401 
402   const nsTArray<RefPtr<SessionHistoryEntry>>& Children() { return mChildren; }
403 
404  private:
405   friend struct LoadingSessionHistoryInfo;
406   virtual ~SessionHistoryEntry();
407 
408   UniquePtr<SessionHistoryInfo> mInfo;
409   nsISHEntry* mParent = nullptr;
410   uint32_t mID;
411   nsTArray<RefPtr<SessionHistoryEntry>> mChildren;
412 
413   bool mForInitialLoad = false;
414 
415   HistoryEntryCounterForBrowsingContext mBCHistoryLength;
416 
417   static nsTHashMap<nsUint64HashKey, SessionHistoryEntry*>* sLoadIdToEntry;
418 };
419 
420 NS_DEFINE_STATIC_IID_ACCESSOR(SessionHistoryEntry, NS_SESSIONHISTORYENTRY_IID)
421 
422 }  // namespace dom
423 
424 namespace ipc {
425 
426 class IProtocol;
427 
428 // Allow sending SessionHistoryInfo objects over IPC.
429 template <>
430 struct IPDLParamTraits<dom::SessionHistoryInfo> {
431   static void Write(IPC::Message* aMsg, IProtocol* aActor,
432                     const dom::SessionHistoryInfo& aParam);
433   static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
434                    IProtocol* aActor, dom::SessionHistoryInfo* aResult);
435 };
436 
437 // Allow sending LoadingSessionHistoryInfo objects over IPC.
438 template <>
439 struct IPDLParamTraits<dom::LoadingSessionHistoryInfo> {
440   static void Write(IPC::Message* aMsg, IProtocol* aActor,
441                     const dom::LoadingSessionHistoryInfo& aParam);
442   static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
443                    IProtocol* aActor, dom::LoadingSessionHistoryInfo* aResult);
444 };
445 
446 // Allow sending nsILayoutHistoryState objects over IPC.
447 template <>
448 struct IPDLParamTraits<nsILayoutHistoryState*> {
449   static void Write(IPC::Message* aMsg, IProtocol* aActor,
450                     nsILayoutHistoryState* aParam);
451   static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
452                    IProtocol* aActor, RefPtr<nsILayoutHistoryState>* aResult);
453 };
454 
455 }  // namespace ipc
456 
457 }  // namespace mozilla
458 
459 #endif /* mozilla_dom_SessionHistoryEntry_h */
460