gecko-dev/dom/base/nsFrameLoader.h
2024-03-07 13:04:21 +00:00

571 lines
19 KiB
C++

/* -*- 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