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