/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */ /* * Class for managing loading of a subframe (creation of the docshell, * handling of loads in it, recursion-checking). */ #ifndef nsFrameLoader_h_ #define nsFrameLoader_h_ #include <cstdint> #include "ErrorList.h" #include "Units.h" #include "js/RootingAPI.h" #include "mozilla/AlreadyAddRefed.h" #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" #include "mozilla/LinkedList.h" #include "mozilla/RefPtr.h" #include "mozilla/dom/BrowsingContext.h" #include "mozilla/dom/Nullable.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/ReferrerPolicyBinding.h" #include "mozilla/dom/WindowProxyHolder.h" #include "mozilla/dom/ipc/IdType.h" #include "mozilla/layers/LayersTypes.h" #include "nsCOMPtr.h" #include "nsCycleCollectionParticipant.h" #include "nsDocShell.h" #include "mozilla/dom/MessageManagerCallback.h" #include "nsID.h" #include "nsIFrame.h" #include "nsIMutationObserver.h" #include "nsISupports.h" #include "nsRect.h" #include "nsStringFwd.h" #include "nsStubMutationObserver.h" #include "nsWrapperCache.h" class nsIURI; class nsSubDocumentFrame; class AutoResetInShow; class AutoResetInFrameSwap; class nsFrameLoaderOwner; class nsIRemoteTab; class nsIDocShellTreeItem; class nsIDocShellTreeOwner; class nsILoadContext; class nsIPrintSettings; class nsIWebBrowserPersistDocumentReceiver; class nsIWebProgressListener; class nsIOpenWindowInfo; namespace mozilla { class OriginAttributes; namespace dom { class ChromeMessageSender; class ContentParent; class Document; class Element; class InProcessBrowserChildMessageManager; class MessageSender; class ProcessMessageManager; class BrowserParent; class MutableTabContext; class BrowserBridgeChild; class RemoteBrowser; struct RemotenessOptions; struct NavigationIsolationOptions; class SessionStoreChild; class SessionStoreParent; struct LazyLoadFrameResumptionState { RefPtr<nsIURI> mBaseURI; ReferrerPolicy mReferrerPolicy = ReferrerPolicy::_empty; void Clear() { mBaseURI = nullptr; mReferrerPolicy = ReferrerPolicy::_empty; } }; namespace ipc { class StructuredCloneData; } // namespace ipc } // namespace dom namespace ipc { class MessageChannel; } // namespace ipc } // namespace mozilla #if defined(MOZ_WIDGET_GTK) typedef struct _GtkWidget GtkWidget; #endif // IID for nsFrameLoader, because some places want to QI to it. #define NS_FRAMELOADER_IID \ { \ 0x297fd0ea, 0x1b4a, 0x4c9a, { \ 0xa4, 0x04, 0xe5, 0x8b, 0xe8, 0x95, 0x10, 0x50 \ } \ } class nsFrameLoader final : public nsStubMutationObserver, public mozilla::dom::ipc::MessageManagerCallback, public nsWrapperCache, public mozilla::LinkedListElement<nsFrameLoader> { friend class AutoResetInShow; friend class AutoResetInFrameSwap; friend class nsFrameLoaderOwner; using Document = mozilla::dom::Document; using Element = mozilla::dom::Element; using BrowserParent = mozilla::dom::BrowserParent; using BrowserBridgeChild = mozilla::dom::BrowserBridgeChild; using BrowsingContext = mozilla::dom::BrowsingContext; using BrowsingContextGroup = mozilla::dom::BrowsingContextGroup; using Promise = mozilla::dom::Promise; public: // Called by Frame Elements to create a new FrameLoader. static already_AddRefed<nsFrameLoader> Create( Element* aOwner, bool aNetworkCreated, nsIOpenWindowInfo* aOpenWindowInfo = nullptr); // Called by nsFrameLoaderOwner::ChangeRemoteness when switching out // FrameLoaders. static already_AddRefed<nsFrameLoader> Recreate( Element* aOwner, BrowsingContext* aContext, BrowsingContextGroup* aGroup, const mozilla::dom::NavigationIsolationOptions& aRemotenessOptions, bool aIsRemote, bool aNetworkCreated, bool aPreserveContext); NS_DECLARE_STATIC_IID_ACCESSOR(NS_FRAMELOADER_IID) NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(nsFrameLoader) NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED nsresult CheckForRecursiveLoad(nsIURI* aURI); nsresult ReallyStartLoading(); void StartDestroy(bool aForProcessSwitch); void DestroyDocShell(); void DestroyComplete(); nsDocShell* GetExistingDocShell() const { return mDocShell; } mozilla::dom::InProcessBrowserChildMessageManager* GetBrowserChildMessageManager() const { return mChildMessageManager; } nsresult UpdatePositionAndSize(nsSubDocumentFrame* aIFrame); void PropagateIsUnderHiddenEmbedderElement( bool aIsUnderHiddenEmbedderElement); void UpdateRemoteStyle(mozilla::StyleImageRendering aImageRendering); // When creating a nsFrameLoaderOwner which is a static clone, a // `nsFrameLoader` is not immediately attached to it. Instead, it is added to // the static clone document's `PendingFrameStaticClones` list. // // After the parent document has been fully cloned, a new frameloader will be // created for the cloned iframe, and `FinishStaticClone` will be called on // it, which will clone the inner document of the source nsFrameLoader. nsresult FinishStaticClone(nsFrameLoader* aStaticCloneOf, nsIPrintSettings* aPrintSettings, bool* aOutHasInProcessPrintCallbacks); nsresult DoRemoteStaticClone(nsFrameLoader* aStaticCloneOf, nsIPrintSettings* aPrintSettings); // WebIDL methods nsDocShell* GetDocShell(mozilla::ErrorResult& aRv); already_AddRefed<nsIRemoteTab> GetRemoteTab(); already_AddRefed<nsILoadContext> GetLoadContext(); mozilla::dom::BrowsingContext* GetBrowsingContext(); mozilla::dom::BrowsingContext* GetExtantBrowsingContext(); mozilla::dom::BrowsingContext* GetMaybePendingBrowsingContext() { return mPendingBrowsingContext; } /** * Start loading the frame. This method figures out what to load * from the owner content in the frame loader. */ void LoadFrame(bool aOriginalSrc); /** * Loads the specified URI in this frame. Behaves identically to loadFrame, * except that this method allows specifying the URI to load. * * @param aURI The URI to load. * @param aTriggeringPrincipal The triggering principal for the load. May be * null, in which case the node principal of the owner content will be * used. * @param aCsp The CSP to be used for the load. That is not the CSP to be * applied to subresources within the frame, but to the iframe load * itself. E.g. if the CSP holds upgrade-insecure-requests the the * frame load is upgraded from http to https. */ nsresult LoadURI(nsIURI* aURI, nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp, bool aOriginalSrc); /** * Resume a redirected load within this frame. * * @param aPendingSwitchID ID of a process-switching load to be reusmed * within this frame. */ void ResumeLoad(uint64_t aPendingSwitchID); /** * Destroy the frame loader and everything inside it. This will * clear the weak owner content reference. */ void Destroy(bool aForProcessSwitch = false); void AsyncDestroy() { mNeedsAsyncDestroy = true; Destroy(); } void RequestUpdatePosition(mozilla::ErrorResult& aRv); already_AddRefed<Promise> RequestTabStateFlush(mozilla::ErrorResult& aRv); void RequestEpochUpdate(uint32_t aEpoch); void RequestSHistoryUpdate(); MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> PrintPreview( nsIPrintSettings* aPrintSettings, BrowsingContext* aSourceBC, mozilla::ErrorResult& aRv); void ExitPrintPreview(); void StartPersistence(BrowsingContext* aContext, nsIWebBrowserPersistDocumentReceiver* aRecv, mozilla::ErrorResult& aRv); // WebIDL getters already_AddRefed<mozilla::dom::MessageSender> GetMessageManager(); already_AddRefed<Element> GetOwnerElement(); uint32_t LazyWidth() const; uint32_t LazyHeight() const; uint64_t ChildID() const { return mChildID; } bool DepthTooGreat() const { return mDepthTooGreat; } bool IsDead() const { return mDestroyCalled; } bool IsNetworkCreated() const { return mNetworkCreated; } nsIContent* GetParentObject() const; /** * MessageManagerCallback methods that we override. */ virtual bool DoLoadMessageManagerScript(const nsAString& aURL, bool aRunInGlobalScope) override; virtual nsresult DoSendAsyncMessage( const nsAString& aMessage, mozilla::dom::ipc::StructuredCloneData& aData) override; /** * Called from the layout frame associated with this frame loader; * this notifies us to hook up with the widget and view. */ MOZ_CAN_RUN_SCRIPT_BOUNDARY bool Show(nsSubDocumentFrame*); void MaybeShowFrame(); /** * Called when the margin properties of the containing frame are changed. */ void MarginsChanged(); /** * Called from the layout frame associated with this frame loader, when * the frame is being torn down; this notifies us that out widget and view * are going away and we should unhook from them. */ void Hide(); // Used when content is causing a FrameLoader to be created, and // needs to try forcing layout to flush in order to get accurate // dimensions for the content area. MOZ_CAN_RUN_SCRIPT_BOUNDARY void ForceLayoutIfNecessary(); // The guts of an nsFrameLoaderOwner::SwapFrameLoader implementation. A // frame loader owner needs to call this, and pass in the two references to // nsRefPtrs for frame loaders that need to be swapped. nsresult SwapWithOtherLoader(nsFrameLoader* aOther, nsFrameLoaderOwner* aThisOwner, nsFrameLoaderOwner* aOtherOwner); nsresult SwapWithOtherRemoteLoader(nsFrameLoader* aOther, nsFrameLoaderOwner* aThisOwner, nsFrameLoaderOwner* aOtherOwner); /** * Return the primary frame for our owning content, or null if it * can't be found. */ nsIFrame* GetPrimaryFrameOfOwningContent() const; /** * Return the document that owns this, or null if we don't have * an owner. */ Document* GetOwnerDoc() const; /** * Returns whether this frame is a remote frame. * * This is true for either a top-level remote browser in the parent process, * or a remote subframe in the child process. */ bool IsRemoteFrame(); mozilla::dom::RemoteBrowser* GetRemoteBrowser() const; /** * Returns the IPDL actor used if this is a top-level remote browser, or null * otherwise. */ BrowserParent* GetBrowserParent() const; /** * Returns the IPDL actor used if this is an out-of-process iframe, or null * otherwise. */ BrowserBridgeChild* GetBrowserBridgeChild() const; /** * Returns the layers ID that this remote frame is using to render. * * This must only be called if this is a remote frame. */ mozilla::layers::LayersId GetLayersId() const; mozilla::dom::ChromeMessageSender* GetFrameMessageManager() { return mMessageManager; } mozilla::dom::Element* GetOwnerContent() { return mOwnerContent; } /** * Stashes a detached nsIFrame on the frame loader. We do this when we're * destroying the nsSubDocumentFrame. If the nsSubdocumentFrame is * being reframed we'll restore the detached nsIFrame when it's recreated, * otherwise we'll discard the old presentation and set the detached * subdoc nsIFrame to null. */ void SetDetachedSubdocFrame(nsIFrame* aDetachedFrame); /** * Retrieves the detached nsIFrame as set by SetDetachedSubdocFrame(). */ nsIFrame* GetDetachedSubdocFrame(bool* aOutIsSet = nullptr) const; /** * Applies a new set of sandbox flags. These are merged with the sandbox * flags from our owning content's owning document with a logical OR, this * ensures that we can only add restrictions and never remove them. */ void ApplySandboxFlags(uint32_t sandboxFlags); void GetURL(nsString& aURL, nsIPrincipal** aTriggeringPrincipal, nsIContentSecurityPolicy** aCsp); // Properly retrieves documentSize of any subdocument type. nsresult GetWindowDimensions(nsIntRect& aRect); virtual mozilla::dom::ProcessMessageManager* GetProcessMessageManager() const override; // public because a callback needs these. RefPtr<mozilla::dom::ChromeMessageSender> mMessageManager; RefPtr<mozilla::dom::InProcessBrowserChildMessageManager> mChildMessageManager; virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override; void SetWillChangeProcess(); // Configure which remote process should be used to host the remote browser // created in `TryRemoteBrowser`. This method _must_ be called before // `TryRemoteBrowser`, and a script blocker must be on the stack. // // |aContentParent|, if set, must have the remote type |aRemoteType|. void ConfigRemoteProcess(const nsACString& aRemoteType, mozilla::dom::ContentParent* aContentParent); // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230) MOZ_CAN_RUN_SCRIPT_BOUNDARY void MaybeNotifyCrashed( mozilla::dom::BrowsingContext* aBrowsingContext, mozilla::dom::ContentParentId aChildID, mozilla::ipc::MessageChannel* aChannel); void FireErrorEvent(); mozilla::dom::SessionStoreChild* GetSessionStoreChild() { return mSessionStoreChild; } mozilla::dom::SessionStoreParent* GetSessionStoreParent(); private: nsFrameLoader(mozilla::dom::Element* aOwner, mozilla::dom::BrowsingContext* aBrowsingContext, bool aIsRemote, bool aNetworkCreated); ~nsFrameLoader(); void SetOwnerContent(mozilla::dom::Element* aContent); /** * Get our owning element's app manifest URL, or return the empty string if * our owning element doesn't have an app manifest URL. */ void GetOwnerAppManifestURL(nsAString& aOut); /** * If we are an IPC frame, set mRemoteFrame. Otherwise, create and * initialize mDocShell. */ nsresult MaybeCreateDocShell(); nsresult EnsureMessageManager(); nsresult ReallyLoadFrameScripts(); nsDocShell* GetDocShell() const { return mDocShell; } void AssertSafeToInit(); // Updates the subdocument position and size. This gets called only // when we have our own in-process DocShell. void UpdateBaseWindowPositionAndSize(nsSubDocumentFrame* aIFrame); /** * Checks whether a load of the given URI should be allowed, and returns an * error result if it should not. * * @param aURI The URI to check. * @param aTriggeringPrincipal The triggering principal for the load. May be * null, in which case the node principal of the owner content is used. */ nsresult CheckURILoad(nsIURI* aURI, nsIPrincipal* aTriggeringPrincipal); nsresult ReallyStartLoadingInternal(); // Returns true if we have a remote browser or else attempts to create a // remote browser and returns true if successful. bool EnsureRemoteBrowser(); // Return true if remote browser created; nothing else to do bool TryRemoteBrowser(); bool TryRemoteBrowserInternal(); // Tell the remote browser that it's now "virtually visible" bool ShowRemoteFrame(const mozilla::ScreenIntSize& size, nsSubDocumentFrame* aFrame = nullptr); void AddTreeItemToTreeOwner(nsIDocShellTreeItem* aItem, nsIDocShellTreeOwner* aOwner); nsresult GetNewTabContext(mozilla::dom::MutableTabContext* aTabContext, nsIURI* aURI = nullptr); enum BrowserParentChange { eBrowserParentRemoved, eBrowserParentChanged }; void MaybeUpdatePrimaryBrowserParent(BrowserParentChange aChange); nsresult PopulateOriginContextIdsFromAttributes( mozilla::OriginAttributes& aAttr); bool EnsureBrowsingContextAttached(); // Invoke the callback from nsOpenWindowInfo to indicate that a // browsing context for a newly opened tab/window is ready. void InvokeBrowsingContextReadyCallback(); void RequestFinalTabStateFlush(); const mozilla::dom::LazyLoadFrameResumptionState& GetLazyLoadFrameResumptionState(); RefPtr<mozilla::dom::BrowsingContext> mPendingBrowsingContext; nsCOMPtr<nsIURI> mURIToLoad; nsCOMPtr<nsIPrincipal> mTriggeringPrincipal; nsCOMPtr<nsIContentSecurityPolicy> mCsp; nsCOMPtr<nsIOpenWindowInfo> mOpenWindowInfo; mozilla::dom::Element* mOwnerContent; // WEAK // After the frameloader has been removed from the DOM but before all of the // messages from the frame have been received, we keep a strong reference to // our <browser> element. RefPtr<mozilla::dom::Element> mOwnerContentStrong; // Stores the root frame of the subdocument while the subdocument is being // reframed. Used to restore the presentation after reframing. WeakFrame mDetachedSubdocFrame; // When performing a process switch, this value is used rather than mURIToLoad // to identify the process-switching load which should be resumed in the // target process. uint64_t mPendingSwitchID; uint64_t mChildID; RefPtr<mozilla::dom::RemoteBrowser> mRemoteBrowser; RefPtr<nsDocShell> mDocShell; // Holds the last known size of the frame. mozilla::ScreenIntSize mLazySize; // Actor for collecting session store data from content children. This will be // cleared and set to null eagerly when taking down the frameloader to break // refcounted cycles early. RefPtr<mozilla::dom::SessionStoreChild> mSessionStoreChild; nsCString mRemoteType; bool mInitialized : 1; bool mDepthTooGreat : 1; bool mIsTopLevelContent : 1; bool mDestroyCalled : 1; bool mNeedsAsyncDestroy : 1; bool mInSwap : 1; bool mInShow : 1; bool mHideCalled : 1; // True when the object is created for an element which the parser has // created using NS_FROM_PARSER_NETWORK flag. If the element is modified, // it may lose the flag. bool mNetworkCreated : 1; // True if a pending load corresponds to the original src (or srcdoc) // attribute of the frame element. bool mLoadingOriginalSrc : 1; bool mRemoteBrowserShown : 1; bool mIsRemoteFrame : 1; // If true, the FrameLoader will be re-created with the same BrowsingContext, // but for a different process, after it is destroyed. bool mWillChangeProcess : 1; bool mObservingOwnerContent : 1; // Whether we had a (possibly dead now) mDetachedSubdocFrame. bool mHadDetachedFrame : 1; // When an out-of-process nsFrameLoader crashes, an event is fired on the // frame. To ensure this is only fired once, this bit is checked. bool mTabProcessCrashFired : 1; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsFrameLoader, NS_FRAMELOADER_IID) inline nsISupports* ToSupports(nsFrameLoader* aFrameLoader) { return aFrameLoader; } #endif