summaryrefslogtreecommitdiffstats
path: root/layout/generic/nsSubDocumentFrame.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/generic/nsSubDocumentFrame.cpp')
-rw-r--r--layout/generic/nsSubDocumentFrame.cpp1454
1 files changed, 1454 insertions, 0 deletions
diff --git a/layout/generic/nsSubDocumentFrame.cpp b/layout/generic/nsSubDocumentFrame.cpp
new file mode 100644
index 0000000000..2f7adccbd7
--- /dev/null
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -0,0 +1,1454 @@
+/* -*- 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/. */
+
+/*
+ * rendering object for replaced elements that contain a document, such
+ * as <frame>, <iframe>, and some <object>s
+ */
+
+#include "nsSubDocumentFrame.h"
+
+#include "mozilla/Preferences.h"
+#include "mozilla/PresShell.h"
+#include "mozilla/StaticPrefs_layout.h"
+#include "mozilla/Unused.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/dom/HTMLFrameElement.h"
+#include "mozilla/dom/BrowserParent.h"
+
+#include "nsCOMPtr.h"
+#include "nsGenericHTMLElement.h"
+#include "nsGenericHTMLFrameElement.h"
+#include "nsAttrValueInlines.h"
+#include "nsIDocShell.h"
+#include "nsIContentViewer.h"
+#include "nsIContentInlines.h"
+#include "nsPresContext.h"
+#include "nsView.h"
+#include "nsViewManager.h"
+#include "nsGkAtoms.h"
+#include "nsStyleConsts.h"
+#include "nsStyleStruct.h"
+#include "nsStyleStructInlines.h"
+#include "nsFrameSetFrame.h"
+#include "nsNameSpaceManager.h"
+#include "nsDisplayList.h"
+#include "nsIScrollableFrame.h"
+#include "nsIObjectLoadingContent.h"
+#include "nsLayoutUtils.h"
+#include "FrameLayerBuilder.h"
+#include "nsPluginFrame.h"
+#include "nsContentUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsQueryObject.h"
+#include "RetainedDisplayListBuilder.h"
+#include "nsObjectLoadingContent.h"
+
+#include "Layers.h"
+#include "BasicLayers.h"
+#include "mozilla/layers/WebRenderUserData.h"
+#include "mozilla/layers/WebRenderScrollData.h"
+#include "mozilla/layers/RenderRootStateManager.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+
+static Document* GetDocumentFromView(nsView* aView) {
+ MOZ_ASSERT(aView, "null view");
+
+ nsViewManager* vm = aView->GetViewManager();
+ PresShell* presShell = vm ? vm->GetPresShell() : nullptr;
+ return presShell ? presShell->GetDocument() : nullptr;
+}
+
+nsSubDocumentFrame::nsSubDocumentFrame(ComputedStyle* aStyle,
+ nsPresContext* aPresContext)
+ : nsAtomicContainerFrame(aStyle, aPresContext, kClassID),
+ mOuterView(nullptr),
+ mInnerView(nullptr),
+ mIsInline(false),
+ mPostedReflowCallback(false),
+ mDidCreateDoc(false),
+ mCallingShow(false) {}
+
+#ifdef ACCESSIBILITY
+a11y::AccType nsSubDocumentFrame::AccessibleType() {
+ return a11y::eOuterDocType;
+}
+#endif
+
+NS_QUERYFRAME_HEAD(nsSubDocumentFrame)
+ NS_QUERYFRAME_ENTRY(nsSubDocumentFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsAtomicContainerFrame)
+
+class AsyncFrameInit : public Runnable {
+ public:
+ explicit AsyncFrameInit(nsIFrame* aFrame)
+ : mozilla::Runnable("AsyncFrameInit"), mFrame(aFrame) {}
+ NS_IMETHOD Run() override {
+ AUTO_PROFILER_LABEL("AsyncFrameInit::Run", OTHER);
+ if (mFrame.IsAlive()) {
+ static_cast<nsSubDocumentFrame*>(mFrame.GetFrame())->ShowViewer();
+ }
+ return NS_OK;
+ }
+
+ private:
+ WeakFrame mFrame;
+};
+
+static void InsertViewsInReverseOrder(nsView* aSibling, nsView* aParent);
+
+static void EndSwapDocShellsForViews(nsView* aView);
+
+void nsSubDocumentFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow) {
+ MOZ_ASSERT(aContent);
+ // determine if we are a <frame> or <iframe>
+ mIsInline = !aContent->IsHTMLElement(nsGkAtoms::frame);
+
+ nsAtomicContainerFrame::Init(aContent, aParent, aPrevInFlow);
+
+ // CreateView() creates this frame's view, stored in mOuterView. It needs to
+ // be created first since it's the parent of the inner view, stored in
+ // mInnerView.
+ CreateView();
+ EnsureInnerView();
+
+ // Set the primary frame now so that nsDocumentViewer::FindContainerView
+ // called from within EndSwapDocShellsForViews below can find it if needed.
+ aContent->SetPrimaryFrame(this);
+
+ // If we have a detached subdoc's root view on our frame loader, re-insert
+ // it into the view tree. This happens when we've been reframed, and
+ // ensures the presentation persists across reframes. If the frame element
+ // has changed documents however, we blow away the presentation.
+ RefPtr<nsFrameLoader> frameloader = FrameLoader();
+ if (frameloader) {
+ nsCOMPtr<Document> oldContainerDoc;
+ nsIFrame* detachedFrame =
+ frameloader->GetDetachedSubdocFrame(getter_AddRefs(oldContainerDoc));
+ frameloader->SetDetachedSubdocFrame(nullptr, nullptr);
+ MOZ_ASSERT(oldContainerDoc || !detachedFrame);
+ if (oldContainerDoc) {
+ nsView* detachedView = detachedFrame ? detachedFrame->GetView() : nullptr;
+ if (detachedView && oldContainerDoc == aContent->OwnerDoc()) {
+ // Restore stashed presentation.
+ ::InsertViewsInReverseOrder(detachedView, mInnerView);
+ ::EndSwapDocShellsForViews(mInnerView->GetFirstChild());
+ } else {
+ // Presentation is for a different document, don't restore it.
+ frameloader->Hide();
+ }
+ }
+ }
+
+ PropagateIsUnderHiddenEmbedderElementToSubView(
+ PresShell()->IsUnderHiddenEmbedderElement() ||
+ !StyleVisibility()->IsVisible());
+
+ nsContentUtils::AddScriptRunner(new AsyncFrameInit(this));
+}
+
+void nsSubDocumentFrame::PropagateIsUnderHiddenEmbedderElementToSubView(
+ bool aIsUnderHiddenEmbedderElement) {
+ if (mFrameLoader && mFrameLoader->IsRemoteFrame()) {
+ mFrameLoader->SendIsUnderHiddenEmbedderElement(
+ aIsUnderHiddenEmbedderElement);
+ return;
+ }
+
+ if (!mInnerView) {
+ return;
+ }
+
+ nsView* subdocView = mInnerView->GetFirstChild();
+ while (subdocView) {
+ if (mozilla::PresShell* presShell = subdocView->GetPresShell()) {
+ presShell->SetIsUnderHiddenEmbedderElement(aIsUnderHiddenEmbedderElement);
+ }
+ subdocView = subdocView->GetNextSibling();
+ }
+}
+
+void nsSubDocumentFrame::ShowViewer() {
+ if (mCallingShow) {
+ return;
+ }
+
+ RefPtr<nsFrameLoader> frameloader = FrameLoader();
+ if (!frameloader) {
+ return;
+ }
+
+ if (!frameloader->IsRemoteFrame() && !PresContext()->IsDynamic()) {
+ // We let the printing code take care of loading the document and
+ // initializing the shell; just create the inner view for it to use.
+ (void)EnsureInnerView();
+ } else {
+ AutoWeakFrame weakThis(this);
+ mCallingShow = true;
+ bool didCreateDoc = frameloader->Show(this);
+ if (!weakThis.IsAlive()) {
+ return;
+ }
+ mCallingShow = false;
+ mDidCreateDoc = didCreateDoc;
+
+ if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
+ frameloader->UpdatePositionAndSize(this);
+ }
+
+ if (!weakThis.IsAlive()) {
+ return;
+ }
+ InvalidateFrame();
+ }
+}
+
+nsIFrame* nsSubDocumentFrame::GetSubdocumentRootFrame() {
+ if (!mInnerView) return nullptr;
+ nsView* subdocView = mInnerView->GetFirstChild();
+ return subdocView ? subdocView->GetFrame() : nullptr;
+}
+
+mozilla::PresShell* nsSubDocumentFrame::GetSubdocumentPresShellForPainting(
+ uint32_t aFlags) {
+ if (!mInnerView) return nullptr;
+
+ nsView* subdocView = mInnerView->GetFirstChild();
+ if (!subdocView) return nullptr;
+
+ mozilla::PresShell* presShell = nullptr;
+
+ nsIFrame* subdocRootFrame = subdocView->GetFrame();
+ if (subdocRootFrame) {
+ presShell = subdocRootFrame->PresShell();
+ }
+
+ // If painting is suppressed in the presshell, we try to look for a better
+ // presshell to use.
+ if (!presShell || (presShell->IsPaintingSuppressed() &&
+ !(aFlags & IGNORE_PAINT_SUPPRESSION))) {
+ // During page transition mInnerView will sometimes have two children, the
+ // first being the new page that may not have any frame, and the second
+ // being the old page that will probably have a frame.
+ nsView* nextView = subdocView->GetNextSibling();
+ nsIFrame* frame = nullptr;
+ if (nextView) {
+ frame = nextView->GetFrame();
+ }
+ if (frame) {
+ mozilla::PresShell* presShellForNextView = frame->PresShell();
+ if (!presShell || (presShellForNextView &&
+ !presShellForNextView->IsPaintingSuppressed() &&
+ StaticPrefs::layout_show_previous_page())) {
+ subdocView = nextView;
+ subdocRootFrame = frame;
+ presShell = presShellForNextView;
+ }
+ }
+ if (!presShell) {
+ // If we don't have a frame we use this roundabout way to get the pres
+ // shell.
+ if (!mFrameLoader) return nullptr;
+ nsIDocShell* docShell = mFrameLoader->GetDocShell(IgnoreErrors());
+ if (!docShell) return nullptr;
+ presShell = docShell->GetPresShell();
+ }
+ }
+
+ return presShell;
+}
+
+ScreenIntSize nsSubDocumentFrame::GetSubdocumentSize() {
+ if (HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
+ if (RefPtr<nsFrameLoader> frameloader = FrameLoader()) {
+ nsCOMPtr<Document> oldContainerDoc;
+ nsIFrame* detachedFrame =
+ frameloader->GetDetachedSubdocFrame(getter_AddRefs(oldContainerDoc));
+ if (nsView* view = detachedFrame ? detachedFrame->GetView() : nullptr) {
+ nsSize size = view->GetBounds().Size();
+ nsPresContext* presContext = detachedFrame->PresContext();
+ return ScreenIntSize(presContext->AppUnitsToDevPixels(size.width),
+ presContext->AppUnitsToDevPixels(size.height));
+ }
+ }
+ // Pick some default size for now. Using 10x10 because that's what the
+ // code used to do.
+ return ScreenIntSize(10, 10);
+ }
+
+ nsSize docSizeAppUnits;
+ nsPresContext* presContext = PresContext();
+ if (GetContent()->IsHTMLElement(nsGkAtoms::frame)) {
+ docSizeAppUnits = GetSize();
+ } else {
+ docSizeAppUnits = GetContentRect().Size();
+ }
+
+ // Adjust subdocument size, according to 'object-fit' and the subdocument's
+ // intrinsic size and ratio.
+ docSizeAppUnits = nsLayoutUtils::ComputeObjectDestRect(
+ nsRect(nsPoint(), docSizeAppUnits), GetIntrinsicSize(),
+ GetIntrinsicRatio(), StylePosition())
+ .Size();
+
+ return ScreenIntSize(
+ presContext->AppUnitsToDevPixels(docSizeAppUnits.width),
+ presContext->AppUnitsToDevPixels(docSizeAppUnits.height));
+}
+
+static void WrapBackgroundColorInOwnLayer(nsDisplayListBuilder* aBuilder,
+ nsIFrame* aFrame,
+ nsDisplayList* aList) {
+ nsDisplayList tempItems;
+ nsDisplayItem* item;
+ while ((item = aList->RemoveBottom()) != nullptr) {
+ if (item->GetType() == DisplayItemType::TYPE_BACKGROUND_COLOR) {
+ nsDisplayList tmpList;
+ tmpList.AppendToTop(item);
+ item = MakeDisplayItemWithIndex<nsDisplayOwnLayer>(
+ aBuilder, aFrame, /* aIndex = */ nsDisplayOwnLayer::OwnLayerForSubdoc,
+ &tmpList, aBuilder->CurrentActiveScrolledRoot(),
+ nsDisplayOwnLayerFlags::None, ScrollbarData{}, true, false);
+ }
+ if (item) {
+ tempItems.AppendToTop(item);
+ }
+ }
+ aList->AppendToTop(&tempItems);
+}
+
+void nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsDisplayListSet& aLists) {
+ if (!IsVisibleForPainting()) {
+ return;
+ }
+
+ nsFrameLoader* frameLoader = FrameLoader();
+ bool isRemoteFrame = frameLoader && frameLoader->IsRemoteFrame();
+
+ // If we are pointer-events:none then we don't need to HitTest background
+ bool pointerEventsNone =
+ StyleUI()->mPointerEvents == StylePointerEvents::None;
+ if (!aBuilder->IsForEventDelivery() || !pointerEventsNone) {
+ nsDisplayListCollection decorations(aBuilder);
+ DisplayBorderBackgroundOutline(aBuilder, decorations);
+ if (isRemoteFrame) {
+ // Wrap background colors of <iframe>s with remote subdocuments in their
+ // own layer so we generate a ColorLayer. This is helpful for optimizing
+ // compositing; we can skip compositing the ColorLayer when the
+ // remote content is opaque.
+ WrapBackgroundColorInOwnLayer(aBuilder, this,
+ decorations.BorderBackground());
+ }
+ decorations.MoveTo(aLists);
+ }
+
+ if (aBuilder->IsForEventDelivery() && pointerEventsNone) {
+ return;
+ }
+
+ // If we're passing pointer events to children then we have to descend into
+ // subdocuments no matter what, to determine which parts are transparent for
+ // hit-testing or event regions.
+ bool needToDescend = aBuilder->GetDescendIntoSubdocuments();
+ if (!mInnerView || !needToDescend) {
+ return;
+ }
+
+ if (isRemoteFrame) {
+ // We're the subdoc for <browser remote="true"> and it has
+ // painted content. Display its shadow layer tree.
+ DisplayListClipState::AutoSaveRestore clipState(aBuilder);
+ clipState.ClipContainingBlockDescendantsToContentBox(aBuilder, this);
+
+ aLists.Content()->AppendNewToTop<nsDisplayRemote>(aBuilder, this);
+ return;
+ }
+
+ RefPtr<mozilla::PresShell> presShell = GetSubdocumentPresShellForPainting(
+ aBuilder->IsIgnoringPaintSuppression() ? IGNORE_PAINT_SUPPRESSION : 0);
+
+ if (!presShell) {
+ return;
+ }
+
+ if (aBuilder->IsInFilter()) {
+ Document* outerDoc = PresShell()->GetDocument();
+ Document* innerDoc = presShell->GetDocument();
+ if (outerDoc && innerDoc) {
+ if (!outerDoc->NodePrincipal()->Equals(innerDoc->NodePrincipal())) {
+ outerDoc->SetUseCounter(eUseCounter_custom_FilteredCrossOriginIFrame);
+ }
+ }
+ }
+
+ nsIFrame* subdocRootFrame = presShell->GetRootFrame();
+
+ nsPresContext* presContext = presShell->GetPresContext();
+
+ int32_t parentAPD = PresContext()->AppUnitsPerDevPixel();
+ int32_t subdocAPD = presContext->AppUnitsPerDevPixel();
+
+ nsRect visible;
+ nsRect dirty;
+ bool ignoreViewportScrolling = false;
+ if (subdocRootFrame) {
+ // get the dirty rect relative to the root frame of the subdoc
+ visible = aBuilder->GetVisibleRect() + GetOffsetToCrossDoc(subdocRootFrame);
+ dirty = aBuilder->GetDirtyRect() + GetOffsetToCrossDoc(subdocRootFrame);
+ // and convert into the appunits of the subdoc
+ visible = visible.ScaleToOtherAppUnitsRoundOut(parentAPD, subdocAPD);
+ dirty = dirty.ScaleToOtherAppUnitsRoundOut(parentAPD, subdocAPD);
+
+ if (nsIScrollableFrame* rootScrollableFrame =
+ presShell->GetRootScrollFrameAsScrollable()) {
+ // Use a copy, so the rects don't get modified.
+ nsRect copyOfDirty = dirty;
+ nsRect copyOfVisible = visible;
+ // TODO(botond): Can we just axe this DecideScrollableLayer call?
+ rootScrollableFrame->DecideScrollableLayer(aBuilder, &copyOfVisible,
+ &copyOfDirty,
+ /* aSetBase = */ true);
+
+ ignoreViewportScrolling = presShell->IgnoringViewportScrolling();
+ }
+
+ aBuilder->EnterPresShell(subdocRootFrame, pointerEventsNone);
+ aBuilder->IncrementPresShellPaintCount(presShell);
+ } else {
+ visible = aBuilder->GetVisibleRect();
+ dirty = aBuilder->GetDirtyRect();
+ }
+
+ DisplayListClipState::AutoSaveRestore clipState(aBuilder);
+ clipState.ClipContainingBlockDescendantsToContentBox(aBuilder, this);
+
+ nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
+ bool constructZoomItem = subdocRootFrame && parentAPD != subdocAPD;
+ bool needsOwnLayer = false;
+ if (constructZoomItem || presContext->IsRootContentDocument() ||
+ (sf && sf->IsScrollingActive(aBuilder))) {
+ needsOwnLayer = true;
+ }
+
+ nsDisplayList childItems;
+
+ {
+ DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
+ if (needsOwnLayer) {
+ // Clear current clip. There's no point in propagating it down, since
+ // the layer we will construct will be clipped by the current clip.
+ // In fact for nsDisplayZoom propagating it down would be incorrect since
+ // nsDisplayZoom changes the meaning of appunits.
+ nestedClipState.Clear();
+ }
+
+ // Invoke AutoBuildingDisplayList to ensure that the correct dirty rect
+ // is used to compute the visible rect if AddCanvasBackgroundColorItem
+ // creates a display item.
+ nsIFrame* frame = subdocRootFrame ? subdocRootFrame : this;
+ nsDisplayListBuilder::AutoBuildingDisplayList building(aBuilder, frame,
+ visible, dirty);
+
+ if (subdocRootFrame) {
+ nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
+ nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(
+ aBuilder,
+ ignoreViewportScrolling && rootScrollFrame &&
+ rootScrollFrame->GetContent()
+ ? nsLayoutUtils::FindOrCreateIDFor(rootScrollFrame->GetContent())
+ : aBuilder->GetCurrentScrollParentId());
+
+ bool hasDocumentLevelListenersForApzAwareEvents =
+ gfxPlatform::AsyncPanZoomEnabled() &&
+ nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(presShell);
+
+ aBuilder->SetAncestorHasApzAwareEventHandler(
+ hasDocumentLevelListenersForApzAwareEvents);
+ subdocRootFrame->BuildDisplayListForStackingContext(aBuilder,
+ &childItems);
+ }
+
+ if (!aBuilder->IsForEventDelivery()) {
+ // If we are going to use a displayzoom below then any items we put under
+ // it need to have underlying frames from the subdocument. So we need to
+ // calculate the bounds based on which frame will be the underlying frame
+ // for the canvas background color item.
+ nsRect bounds =
+ GetContentRectRelativeToSelf() + aBuilder->ToReferenceFrame(this);
+ if (subdocRootFrame) {
+ bounds = bounds.ScaleToOtherAppUnitsRoundOut(parentAPD, subdocAPD);
+ }
+
+ // If we are in print preview/page layout we want to paint the grey
+ // background behind the page, not the canvas color. The canvas color gets
+ // painted on the page itself.
+ if (nsLayoutUtils::NeedsPrintPreviewBackground(presContext)) {
+ presShell->AddPrintPreviewBackgroundItem(aBuilder, &childItems, frame,
+ bounds);
+ } else {
+ // Add the canvas background color to the bottom of the list. This
+ // happens after we've built the list so that
+ // AddCanvasBackgroundColorItem can monkey with the contents if
+ // necessary.
+ AddCanvasBackgroundColorFlags flags =
+ AddCanvasBackgroundColorFlags::ForceDraw |
+ AddCanvasBackgroundColorFlags::AddForSubDocument;
+ presShell->AddCanvasBackgroundColorItem(
+ aBuilder, &childItems, frame, bounds, NS_RGBA(0, 0, 0, 0), flags);
+ }
+ }
+ }
+
+ if (subdocRootFrame) {
+ aBuilder->LeavePresShell(subdocRootFrame, &childItems);
+ }
+
+ // Generate a resolution and/or zoom item if needed. If one or both of those
+ // is created, we don't need to create a separate nsDisplaySubDocument.
+
+ nsDisplayOwnLayerFlags flags =
+ nsDisplayOwnLayerFlags::GenerateSubdocInvalidations;
+ // If ignoreViewportScrolling is true then the top most layer we create here
+ // is going to become the scrollable layer for the root scroll frame, so we
+ // want to add nsDisplayOwnLayer::GENERATE_SCROLLABLE_LAYER to whatever layer
+ // becomes the topmost. We do this below.
+ if (constructZoomItem) {
+ nsDisplayOwnLayerFlags zoomFlags = flags;
+ if (ignoreViewportScrolling) {
+ zoomFlags |= nsDisplayOwnLayerFlags::GenerateScrollableLayer;
+ }
+ childItems.AppendNewToTop<nsDisplayZoom>(aBuilder, subdocRootFrame, this,
+ &childItems, subdocAPD, parentAPD,
+ zoomFlags);
+
+ needsOwnLayer = false;
+ }
+ // Wrap the zoom item in the resolution item if we have both because we want
+ // the resolution scale applied on top of the app units per dev pixel
+ // conversion.
+ if (ignoreViewportScrolling) {
+ flags |= nsDisplayOwnLayerFlags::GenerateScrollableLayer;
+ }
+
+ // We always want top level content documents to be in their own layer.
+ nsDisplaySubDocument* layerItem = MakeDisplayItem<nsDisplaySubDocument>(
+ aBuilder, subdocRootFrame ? subdocRootFrame : this, this, &childItems,
+ flags);
+ if (layerItem) {
+ childItems.AppendToTop(layerItem);
+ layerItem->SetShouldFlattenAway(!needsOwnLayer);
+ }
+
+ if (aBuilder->IsForFrameVisibility()) {
+ // We don't add the childItems to the return list as we're dealing with them
+ // here.
+ presShell->RebuildApproximateFrameVisibilityDisplayList(childItems);
+ childItems.DeleteAll(aBuilder);
+ } else {
+ aLists.Content()->AppendToTop(&childItems);
+ }
+}
+
+#ifdef DEBUG_FRAME_DUMP
+void nsSubDocumentFrame::List(FILE* out, const char* aPrefix,
+ ListFlags aFlags) const {
+ nsCString str;
+ ListGeneric(str, aPrefix, aFlags);
+ fprintf_stderr(out, "%s\n", str.get());
+
+ if (aFlags.contains(ListFlag::TraverseSubdocumentFrames)) {
+ nsSubDocumentFrame* f = const_cast<nsSubDocumentFrame*>(this);
+ nsIFrame* subdocRootFrame = f->GetSubdocumentRootFrame();
+ if (subdocRootFrame) {
+ nsCString pfx(aPrefix);
+ pfx += " ";
+ subdocRootFrame->List(out, pfx.get(), aFlags);
+ }
+ }
+}
+
+nsresult nsSubDocumentFrame::GetFrameName(nsAString& aResult) const {
+ return MakeFrameName(u"FrameOuter"_ns, aResult);
+}
+#endif
+
+/* virtual */
+nscoord nsSubDocumentFrame::GetMinISize(gfxContext* aRenderingContext) {
+ nscoord result;
+ DISPLAY_MIN_INLINE_SIZE(this, result);
+
+ nsCOMPtr<nsIObjectLoadingContent> iolc = do_QueryInterface(mContent);
+ auto olc = static_cast<nsObjectLoadingContent*>(iolc.get());
+
+ if (olc && olc->GetSubdocumentIntrinsicSize()) {
+ // The subdocument is an SVG document, so technically we should call
+ // SVGOuterSVGFrame::GetMinISize() on its root frame. That method always
+ // returns 0, though, so we can just do that & don't need to bother with
+ // the cross-doc communication.
+ result = 0;
+ } else {
+ result = GetIntrinsicISize();
+ }
+
+ return result;
+}
+
+/* virtual */
+nscoord nsSubDocumentFrame::GetPrefISize(gfxContext* aRenderingContext) {
+ nscoord result;
+ DISPLAY_PREF_INLINE_SIZE(this, result);
+
+ // If the subdocument is an SVG document, then in theory we want to return
+ // the same thing that SVGOuterSVGFrame::GetPrefISize does. That method
+ // has some special handling of percentage values to avoid unhelpful zero
+ // sizing in the presence of orthogonal writing modes. We don't bother
+ // with that for SVG documents in <embed> and <object>, since that special
+ // handling doesn't look up across document boundaries anyway.
+ result = GetIntrinsicISize();
+
+ return result;
+}
+
+/* virtual */
+IntrinsicSize nsSubDocumentFrame::GetIntrinsicSize() {
+ if (StyleDisplay()->IsContainSize()) {
+ // Intrinsic size of 'contain:size' replaced elements is 0,0.
+ return IntrinsicSize(0, 0);
+ }
+
+ if (nsCOMPtr<nsIObjectLoadingContent> iolc = do_QueryInterface(mContent)) {
+ auto olc = static_cast<nsObjectLoadingContent*>(iolc.get());
+
+ if (auto size = olc->GetSubdocumentIntrinsicSize()) {
+ // Use the intrinsic size from the child SVG document, if available.
+ return *size;
+ }
+ }
+
+ if (!IsInline()) {
+ return {}; // <frame> elements have no useful intrinsic size.
+ }
+
+ if (mContent->IsXULElement()) {
+ return {}; // XUL <iframe> and <browser> have no useful intrinsic size
+ }
+
+ // We must be an HTML <iframe>. Return fallback size.
+ return IntrinsicSize(kFallbackIntrinsicSize);
+}
+
+/* virtual */
+AspectRatio nsSubDocumentFrame::GetIntrinsicRatio() const {
+ // FIXME(emilio): This should probably respect contain: size and return no
+ // ratio in the case subDocRoot is non-null. Otherwise we do it by virtue of
+ // using a zero-size below and reusing GetIntrinsicSize().
+ if (nsCOMPtr<nsIObjectLoadingContent> iolc = do_QueryInterface(mContent)) {
+ auto olc = static_cast<nsObjectLoadingContent*>(iolc.get());
+
+ auto ratio = olc->GetSubdocumentIntrinsicRatio();
+ if (ratio && *ratio) {
+ // Use the intrinsic aspect ratio from the child SVG document, if
+ // available.
+ return *ratio;
+ }
+ }
+
+ // NOTE(emilio): Even though we have an intrinsic size, we may not have an
+ // intrinsic ratio. For example `<iframe style="width: 100px">` should not
+ // shrink in the vertical axis to preserve the 300x150 ratio.
+ return nsAtomicContainerFrame::GetIntrinsicRatio();
+}
+
+/* virtual */
+LogicalSize nsSubDocumentFrame::ComputeAutoSize(
+ gfxContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize,
+ nscoord aAvailableISize, const LogicalSize& aMargin,
+ const LogicalSize& aBorderPadding, ComputeSizeFlags aFlags) {
+ if (!IsInline()) {
+ return nsIFrame::ComputeAutoSize(aRenderingContext, aWM, aCBSize,
+ aAvailableISize, aMargin, aBorderPadding,
+ aFlags);
+ }
+
+ const WritingMode wm = GetWritingMode();
+ LogicalSize result(wm, GetIntrinsicISize(), GetIntrinsicBSize());
+ return result.ConvertTo(aWM, wm);
+}
+
+/* virtual */
+nsIFrame::SizeComputationResult nsSubDocumentFrame::ComputeSize(
+ gfxContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize,
+ nscoord aAvailableISize, const LogicalSize& aMargin,
+ const LogicalSize& aBorderPadding, ComputeSizeFlags aFlags) {
+ return {ComputeSizeWithIntrinsicDimensions(
+ aRenderingContext, aWM, GetIntrinsicSize(), GetAspectRatio(),
+ aCBSize, aMargin, aBorderPadding, aFlags),
+ AspectRatioUsage::None};
+}
+
+void nsSubDocumentFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus) {
+ MarkInReflow();
+ DO_GLOBAL_REFLOW_COUNT("nsSubDocumentFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+ MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
+ NS_FRAME_TRACE(
+ NS_FRAME_TRACE_CALLS,
+ ("enter nsSubDocumentFrame::Reflow: maxSize=%d,%d",
+ aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()));
+
+ NS_ASSERTION(aReflowInput.ComputedWidth() != NS_UNCONSTRAINEDSIZE,
+ "Shouldn't have unconstrained stuff here "
+ "thanks to the rules of reflow");
+ NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aReflowInput.ComputedHeight(),
+ "Shouldn't have unconstrained stuff here "
+ "thanks to ComputeAutoSize");
+
+ NS_ASSERTION(mContent->GetPrimaryFrame() == this, "Shouldn't happen");
+
+ // XUL <iframe> or <browser>, or HTML <iframe>, <object> or <embed>
+ const auto wm = aReflowInput.GetWritingMode();
+ aDesiredSize.SetSize(wm, aReflowInput.ComputedSizeWithBorderPadding(wm));
+
+ // "offset" is the offset of our content area from our frame's
+ // top-left corner.
+ nsPoint offset = nsPoint(aReflowInput.ComputedPhysicalBorderPadding().left,
+ aReflowInput.ComputedPhysicalBorderPadding().top);
+
+ if (mInnerView) {
+ const nsMargin& bp = aReflowInput.ComputedPhysicalBorderPadding();
+ nsSize innerSize(aDesiredSize.Width() - bp.LeftRight(),
+ aDesiredSize.Height() - bp.TopBottom());
+
+ // Size & position the view according to 'object-fit' & 'object-position'.
+ nsRect destRect = nsLayoutUtils::ComputeObjectDestRect(
+ nsRect(offset, innerSize), GetIntrinsicSize(), GetIntrinsicRatio(),
+ StylePosition());
+
+ nsViewManager* vm = mInnerView->GetViewManager();
+ vm->MoveViewTo(mInnerView, destRect.x, destRect.y);
+ vm->ResizeView(mInnerView, nsRect(nsPoint(0, 0), destRect.Size()), true);
+ }
+
+ aDesiredSize.SetOverflowAreasToDesiredBounds();
+
+ FinishAndStoreOverflow(&aDesiredSize);
+
+ if (!aPresContext->IsPaginated() && !mPostedReflowCallback) {
+ PresShell()->PostReflowCallback(this);
+ mPostedReflowCallback = true;
+ }
+
+ NS_FRAME_TRACE(
+ NS_FRAME_TRACE_CALLS,
+ ("exit nsSubDocumentFrame::Reflow: size=%d,%d status=%s",
+ aDesiredSize.Width(), aDesiredSize.Height(), ToString(aStatus).c_str()));
+
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
+}
+
+bool nsSubDocumentFrame::ReflowFinished() {
+ RefPtr<nsFrameLoader> frameloader = FrameLoader();
+ if (frameloader) {
+ AutoWeakFrame weakFrame(this);
+
+ frameloader->UpdatePositionAndSize(this);
+
+ if (weakFrame.IsAlive()) {
+ // Make sure that we can post a reflow callback in the future.
+ mPostedReflowCallback = false;
+ }
+ } else {
+ mPostedReflowCallback = false;
+ }
+ return false;
+}
+
+void nsSubDocumentFrame::ReflowCallbackCanceled() {
+ mPostedReflowCallback = false;
+}
+
+nsresult nsSubDocumentFrame::AttributeChanged(int32_t aNameSpaceID,
+ nsAtom* aAttribute,
+ int32_t aModType) {
+ if (aNameSpaceID != kNameSpaceID_None) {
+ return NS_OK;
+ }
+
+ // If the noResize attribute changes, dis/allow frame to be resized
+ if (aAttribute == nsGkAtoms::noresize) {
+ // Note that we're not doing content type checks, but that's ok -- if
+ // they'd fail we will just end up with a null framesetFrame.
+ if (mContent->GetParent()->IsHTMLElement(nsGkAtoms::frameset)) {
+ nsIFrame* parentFrame = GetParent();
+
+ if (parentFrame) {
+ // There is no interface for nsHTMLFramesetFrame so QI'ing to
+ // concrete class, yay!
+ nsHTMLFramesetFrame* framesetFrame = do_QueryFrame(parentFrame);
+ if (framesetFrame) {
+ framesetFrame->RecalculateBorderResize();
+ }
+ }
+ }
+ } else if (aAttribute == nsGkAtoms::marginwidth ||
+ aAttribute == nsGkAtoms::marginheight) {
+ // Notify the frameloader
+ if (RefPtr<nsFrameLoader> frameloader = FrameLoader()) {
+ frameloader->MarginsChanged();
+ }
+ }
+
+ return NS_OK;
+}
+
+void nsSubDocumentFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
+ nsAtomicContainerFrame::DidSetComputedStyle(aOldComputedStyle);
+
+ // If this presshell has invisible ancestors, we don't need to propagate the
+ // visibility style change to the subdocument since the subdocument should
+ // have already set the IsUnderHiddenEmbedderElement flag in
+ // nsSubDocumentFrame::Init.
+ if (PresShell()->IsUnderHiddenEmbedderElement()) {
+ return;
+ }
+
+ const bool isVisible = StyleVisibility()->IsVisible();
+ if (!aOldComputedStyle ||
+ isVisible != aOldComputedStyle->StyleVisibility()->IsVisible()) {
+ PropagateIsUnderHiddenEmbedderElementToSubView(!isVisible);
+ }
+}
+
+nsIFrame* NS_NewSubDocumentFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
+ return new (aPresShell)
+ nsSubDocumentFrame(aStyle, aPresShell->GetPresContext());
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsSubDocumentFrame)
+
+class nsHideViewer : public Runnable {
+ public:
+ nsHideViewer(nsIContent* aFrameElement, nsFrameLoader* aFrameLoader,
+ PresShell* aPresShell, bool aHideViewerIfFrameless)
+ : mozilla::Runnable("nsHideViewer"),
+ mFrameElement(aFrameElement),
+ mFrameLoader(aFrameLoader),
+ mPresShell(aPresShell),
+ mHideViewerIfFrameless(aHideViewerIfFrameless) {
+ NS_ASSERTION(mFrameElement, "Must have a frame element");
+ NS_ASSERTION(mFrameLoader, "Must have a frame loader");
+ NS_ASSERTION(mPresShell, "Must have a presshell");
+ }
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override {
+ // Flush frames, to ensure any pending display:none changes are made.
+ // Note it can be unsafe to flush if we've destroyed the presentation
+ // for some other reason, like if we're shutting down.
+ //
+ // But avoid the flush if we know for sure we're away, like when we're out
+ // of the document already.
+ //
+ // FIXME(emilio): This could still be a perf footgun when removing lots of
+ // siblings where each of them cause the reframe of an ancestor which happen
+ // to contain a subdocument.
+ //
+ // We should find some way to avoid that!
+ if (!mPresShell->IsDestroying() && mFrameElement->IsInComposedDoc()) {
+ MOZ_KnownLive(mPresShell)->FlushPendingNotifications(FlushType::Frames);
+ }
+
+ // Either the frame has been constructed by now, or it never will be,
+ // either way we want to clear the stashed views.
+ mFrameLoader->SetDetachedSubdocFrame(nullptr, nullptr);
+
+ nsSubDocumentFrame* frame = do_QueryFrame(mFrameElement->GetPrimaryFrame());
+ if ((!frame && mHideViewerIfFrameless) || mPresShell->IsDestroying()) {
+ // Either the frame element has no nsIFrame or the presshell is being
+ // destroyed. Hide the nsFrameLoader, which destroys the presentation.
+ mFrameLoader->Hide();
+ }
+ return NS_OK;
+ }
+
+ private:
+ nsCOMPtr<nsIContent> mFrameElement;
+ RefPtr<nsFrameLoader> mFrameLoader;
+ RefPtr<PresShell> mPresShell;
+ bool mHideViewerIfFrameless;
+};
+
+static nsView* BeginSwapDocShellsForViews(nsView* aSibling);
+
+void nsSubDocumentFrame::DestroyFrom(nsIFrame* aDestructRoot,
+ PostDestroyData& aPostDestroyData) {
+ if (mPostedReflowCallback) {
+ PresShell()->CancelReflowCallback(this);
+ mPostedReflowCallback = false;
+ }
+
+ // Detach the subdocument's views and stash them in the frame loader.
+ // We can then reattach them if we're being reframed (for example if
+ // the frame has been made position:fixed).
+ RefPtr<nsFrameLoader> frameloader = FrameLoader();
+ if (frameloader) {
+ nsView* detachedViews =
+ ::BeginSwapDocShellsForViews(mInnerView->GetFirstChild());
+
+ if (detachedViews && detachedViews->GetFrame()) {
+ frameloader->SetDetachedSubdocFrame(detachedViews->GetFrame(),
+ mContent->OwnerDoc());
+
+ // We call nsFrameLoader::HideViewer() in a script runner so that we can
+ // safely determine whether the frame is being reframed or destroyed.
+ nsContentUtils::AddScriptRunner(new nsHideViewer(
+ mContent, frameloader, PresShell(), (mDidCreateDoc || mCallingShow)));
+ } else {
+ frameloader->SetDetachedSubdocFrame(nullptr, nullptr);
+ if (mDidCreateDoc || mCallingShow) {
+ frameloader->Hide();
+ }
+ }
+ }
+
+ nsAtomicContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
+}
+
+nsFrameLoader* nsSubDocumentFrame::FrameLoader() const {
+ if (mFrameLoader) {
+ return mFrameLoader;
+ }
+
+ if (RefPtr<nsFrameLoaderOwner> loaderOwner = do_QueryObject(GetContent())) {
+ mFrameLoader = loaderOwner->GetFrameLoader();
+ }
+
+ return mFrameLoader;
+}
+
+void nsSubDocumentFrame::ResetFrameLoader() {
+ mFrameLoader = nullptr;
+ ClearDisplayItems();
+ nsContentUtils::AddScriptRunner(new AsyncFrameInit(this));
+}
+
+// XXX this should be called ObtainDocShell or something like that,
+// to indicate that it could have side effects
+nsIDocShell* nsSubDocumentFrame::GetDocShell() const {
+ // How can FrameLoader() return null???
+ if (NS_WARN_IF(!FrameLoader())) {
+ return nullptr;
+ }
+ return mFrameLoader->GetDocShell(IgnoreErrors());
+}
+
+static void DestroyDisplayItemDataForFrames(nsIFrame* aFrame) {
+ FrameLayerBuilder::DestroyDisplayItemDataFor(aFrame);
+
+ for (const auto& childList : aFrame->ChildLists()) {
+ for (nsIFrame* child : childList.mList) {
+ DestroyDisplayItemDataForFrames(child);
+ }
+ }
+}
+
+static CallState BeginSwapDocShellsForDocument(Document& aDocument) {
+ if (PresShell* presShell = aDocument.GetPresShell()) {
+ // Disable painting while the views are detached, see bug 946929.
+ presShell->SetNeverPainting(true);
+
+ if (nsIFrame* rootFrame = presShell->GetRootFrame()) {
+ ::DestroyDisplayItemDataForFrames(rootFrame);
+ }
+ }
+ aDocument.EnumerateActivityObservers(nsPluginFrame::BeginSwapDocShells);
+ aDocument.EnumerateSubDocuments(BeginSwapDocShellsForDocument);
+ return CallState::Continue;
+}
+
+static nsView* BeginSwapDocShellsForViews(nsView* aSibling) {
+ // Collect the removed sibling views in reverse order in 'removedViews'.
+ nsView* removedViews = nullptr;
+ while (aSibling) {
+ if (Document* doc = ::GetDocumentFromView(aSibling)) {
+ ::BeginSwapDocShellsForDocument(*doc);
+ }
+ nsView* next = aSibling->GetNextSibling();
+ aSibling->GetViewManager()->RemoveChild(aSibling);
+ aSibling->SetNextSibling(removedViews);
+ removedViews = aSibling;
+ aSibling = next;
+ }
+ return removedViews;
+}
+
+static void InsertViewsInReverseOrder(nsView* aSibling, nsView* aParent) {
+ MOZ_ASSERT(aParent, "null view");
+ MOZ_ASSERT(!aParent->GetFirstChild(), "inserting into non-empty list");
+
+ nsViewManager* vm = aParent->GetViewManager();
+ while (aSibling) {
+ nsView* next = aSibling->GetNextSibling();
+ aSibling->SetNextSibling(nullptr);
+ // true means 'after' in document order which is 'before' in view order,
+ // so this call prepends the child, thus reversing the siblings as we go.
+ vm->InsertChild(aParent, aSibling, nullptr, true);
+ aSibling = next;
+ }
+}
+
+nsresult nsSubDocumentFrame::BeginSwapDocShells(nsIFrame* aOther) {
+ if (!aOther || !aOther->IsSubDocumentFrame()) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ nsSubDocumentFrame* other = static_cast<nsSubDocumentFrame*>(aOther);
+ if (!mFrameLoader || !mDidCreateDoc || mCallingShow || !other->mFrameLoader ||
+ !other->mDidCreateDoc) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ ClearDisplayItems();
+ other->ClearDisplayItems();
+
+ if (mInnerView && other->mInnerView) {
+ nsView* ourSubdocViews = mInnerView->GetFirstChild();
+ nsView* ourRemovedViews = ::BeginSwapDocShellsForViews(ourSubdocViews);
+ nsView* otherSubdocViews = other->mInnerView->GetFirstChild();
+ nsView* otherRemovedViews = ::BeginSwapDocShellsForViews(otherSubdocViews);
+
+ ::InsertViewsInReverseOrder(ourRemovedViews, other->mInnerView);
+ ::InsertViewsInReverseOrder(otherRemovedViews, mInnerView);
+ }
+ mFrameLoader.swap(other->mFrameLoader);
+ return NS_OK;
+}
+
+static CallState EndSwapDocShellsForDocument(Document& aDocument) {
+ // Our docshell and view trees have been updated for the new hierarchy.
+ // Now also update all nsDeviceContext::mWidget to that of the
+ // container view in the new hierarchy.
+ if (nsCOMPtr<nsIDocShell> ds = aDocument.GetDocShell()) {
+ nsCOMPtr<nsIContentViewer> cv;
+ ds->GetContentViewer(getter_AddRefs(cv));
+ while (cv) {
+ RefPtr<nsPresContext> pc = cv->GetPresContext();
+ if (pc && pc->GetPresShell()) {
+ pc->GetPresShell()->SetNeverPainting(ds->IsInvisible());
+ }
+ nsDeviceContext* dc = pc ? pc->DeviceContext() : nullptr;
+ if (dc) {
+ nsView* v = cv->FindContainerView();
+ dc->Init(v ? v->GetNearestWidget(nullptr) : nullptr);
+ }
+ cv = cv->GetPreviousViewer();
+ }
+ }
+
+ aDocument.EnumerateActivityObservers(nsPluginFrame::EndSwapDocShells);
+ aDocument.EnumerateSubDocuments(EndSwapDocShellsForDocument);
+ return CallState::Continue;
+}
+
+static void EndSwapDocShellsForViews(nsView* aSibling) {
+ for (; aSibling; aSibling = aSibling->GetNextSibling()) {
+ if (Document* doc = ::GetDocumentFromView(aSibling)) {
+ ::EndSwapDocShellsForDocument(*doc);
+ }
+ nsIFrame* frame = aSibling->GetFrame();
+ if (frame) {
+ nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(frame);
+ if (parent->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
+ nsIFrame::AddInPopupStateBitToDescendants(frame);
+ } else {
+ nsIFrame::RemoveInPopupStateBitFromDescendants(frame);
+ }
+ if (frame->HasInvalidFrameInSubtree()) {
+ while (parent &&
+ !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT |
+ NS_FRAME_IS_NONDISPLAY)) {
+ parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
+ parent = nsLayoutUtils::GetCrossDocParentFrame(parent);
+ }
+ }
+ }
+ }
+}
+
+void nsSubDocumentFrame::EndSwapDocShells(nsIFrame* aOther) {
+ nsSubDocumentFrame* other = static_cast<nsSubDocumentFrame*>(aOther);
+ AutoWeakFrame weakThis(this);
+ AutoWeakFrame weakOther(aOther);
+
+ if (mInnerView) {
+ ::EndSwapDocShellsForViews(mInnerView->GetFirstChild());
+ }
+ if (other->mInnerView) {
+ ::EndSwapDocShellsForViews(other->mInnerView->GetFirstChild());
+ }
+
+ // Now make sure we reflow both frames, in case their contents
+ // determine their size.
+ // And repaint them, for good measure, in case there's nothing
+ // interesting that happens during reflow.
+ if (weakThis.IsAlive()) {
+ PresShell()->FrameNeedsReflow(this, IntrinsicDirty::TreeChange,
+ NS_FRAME_IS_DIRTY);
+ InvalidateFrameSubtree();
+ PropagateIsUnderHiddenEmbedderElementToSubView(
+ PresShell()->IsUnderHiddenEmbedderElement() ||
+ !StyleVisibility()->IsVisible());
+ }
+ if (weakOther.IsAlive()) {
+ other->PresShell()->FrameNeedsReflow(other, IntrinsicDirty::TreeChange,
+ NS_FRAME_IS_DIRTY);
+ other->InvalidateFrameSubtree();
+ other->PropagateIsUnderHiddenEmbedderElementToSubView(
+ other->PresShell()->IsUnderHiddenEmbedderElement() ||
+ !other->StyleVisibility()->IsVisible());
+ }
+}
+
+void nsSubDocumentFrame::ClearDisplayItems() {
+ DisplayItemArray* items = GetProperty(DisplayItems());
+ if (!items) {
+ return;
+ }
+
+ nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
+ MOZ_ASSERT(displayRoot);
+
+ RetainedDisplayListBuilder* retainedBuilder =
+ displayRoot->GetProperty(RetainedDisplayListBuilder::Cached());
+ MOZ_ASSERT(retainedBuilder);
+
+ for (nsDisplayItemBase* i : *items) {
+ if (i->GetType() == DisplayItemType::TYPE_SUBDOCUMENT) {
+ auto* item = static_cast<nsDisplaySubDocument*>(i);
+ item->GetChildren()->DeleteAll(retainedBuilder->Builder());
+ item->Disown();
+ break;
+ }
+ }
+}
+
+nsView* nsSubDocumentFrame::EnsureInnerView() {
+ if (mInnerView) {
+ return mInnerView;
+ }
+
+ // create, init, set the parent of the view
+ nsView* outerView = GetView();
+ NS_ASSERTION(outerView, "Must have an outer view already");
+ nsRect viewBounds(0, 0, 0, 0); // size will be fixed during reflow
+
+ nsViewManager* viewMan = outerView->GetViewManager();
+ nsView* innerView = viewMan->CreateView(viewBounds, outerView);
+ if (!innerView) {
+ NS_ERROR("Could not create inner view");
+ return nullptr;
+ }
+ mInnerView = innerView;
+ viewMan->InsertChild(outerView, innerView, nullptr, true);
+
+ return mInnerView;
+}
+
+nsPoint nsSubDocumentFrame::GetExtraOffset() const {
+ MOZ_ASSERT(mInnerView);
+ return mInnerView->GetPosition();
+}
+
+void nsSubDocumentFrame::SubdocumentIntrinsicSizeOrRatioChanged() {
+ if (MOZ_UNLIKELY(HasAllStateBits(NS_FRAME_IS_DIRTY))) {
+ // We will be reflowed soon anyway.
+ return;
+ }
+
+ const nsStylePosition* pos = StylePosition();
+ bool dependsOnIntrinsics =
+ !pos->mWidth.ConvertsToLength() || !pos->mHeight.ConvertsToLength();
+
+ if (dependsOnIntrinsics || pos->mObjectFit != StyleObjectFit::Fill) {
+ auto dirtyHint = dependsOnIntrinsics ? IntrinsicDirty::StyleChange
+ : IntrinsicDirty::Resize;
+ PresShell()->FrameNeedsReflow(this, dirtyHint, NS_FRAME_IS_DIRTY);
+ }
+}
+
+/**
+ * Gets the layer-pixel offset of aContainerFrame's content rect top-left
+ * from the nearest display item reference frame (which we assume will be
+ * inducing a ContainerLayer).
+ */
+static nsPoint GetContentRectLayerOffset(nsIFrame* aContainerFrame,
+ nsDisplayListBuilder* aBuilder) {
+ // Offset to the content rect in case we have borders or padding
+ // Note that aContainerFrame could be a reference frame itself, so
+ // we need to be careful here to ensure that we call ToReferenceFrame
+ // on aContainerFrame and not its parent.
+ nsPoint frameOffset =
+ aBuilder->ToReferenceFrame(aContainerFrame) +
+ aContainerFrame->GetContentRectRelativeToSelf().TopLeft();
+
+ return frameOffset;
+}
+
+// Return true iff |aManager| is a "temporary layer manager". They're
+// used for small software rendering tasks, like drawWindow. That's
+// currently implemented by a BasicLayerManager without a backing
+// widget, and hence in non-retained mode.
+inline static bool IsTempLayerManager(LayerManager* aManager) {
+ return (LayersBackend::LAYERS_BASIC == aManager->GetBackendType() &&
+ !static_cast<BasicLayerManager*>(aManager)->IsRetained());
+}
+
+nsDisplayRemote::nsDisplayRemote(nsDisplayListBuilder* aBuilder,
+ nsSubDocumentFrame* aFrame)
+ : nsPaintedDisplayItem(aBuilder, aFrame),
+ mTabId{0},
+ mEventRegionsOverride(EventRegionsOverride::NoOverride) {
+ bool frameIsPointerEventsNone = aFrame->StyleUI()->GetEffectivePointerEvents(
+ aFrame) == StylePointerEvents::None;
+ if (aBuilder->IsInsidePointerEventsNoneDoc() || frameIsPointerEventsNone) {
+ mEventRegionsOverride |= EventRegionsOverride::ForceEmptyHitRegion;
+ }
+ if (nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(
+ aFrame->PresShell())) {
+ mEventRegionsOverride |= EventRegionsOverride::ForceDispatchToContent;
+ }
+
+ nsFrameLoader* frameLoader = GetFrameLoader();
+ MOZ_ASSERT(frameLoader && frameLoader->IsRemoteFrame());
+ if (frameLoader->GetRemoteBrowser()) {
+ mLayersId = frameLoader->GetLayersId();
+ mTabId = frameLoader->GetRemoteBrowser()->GetTabId();
+ }
+}
+
+mozilla::LayerState nsDisplayRemote::GetLayerState(
+ nsDisplayListBuilder* aBuilder, LayerManager* aManager,
+ const ContainerLayerParameters& aParameters) {
+ if (IsTempLayerManager(aManager)) {
+ return mozilla::LayerState::LAYER_NONE;
+ }
+ return mozilla::LayerState::LAYER_ACTIVE_FORCE;
+}
+
+LayerIntSize GetFrameSize(const nsIFrame* aFrame) {
+ LayoutDeviceSize size = LayoutDeviceRect::FromAppUnits(
+ aFrame->GetContentRectRelativeToSelf().Size(),
+ aFrame->PresContext()->AppUnitsPerDevPixel());
+
+ float cumulativeResolution = aFrame->PresShell()->GetCumulativeResolution();
+ return LayerIntSize::Round(size.width * cumulativeResolution,
+ size.height * cumulativeResolution);
+}
+
+already_AddRefed<mozilla::layers::Layer> nsDisplayRemote::BuildLayer(
+ nsDisplayListBuilder* aBuilder, LayerManager* aManager,
+ const ContainerLayerParameters& aContainerParameters) {
+ MOZ_ASSERT(mFrame, "Makes no sense to have a shadow tree without a frame");
+
+ if (IsTempLayerManager(aManager)) {
+ // This can happen if aManager is a "temporary" manager, or if the
+ // widget's layer manager changed out from under us. We need to
+ // FIXME handle the former case somehow, probably with an API to
+ // draw a manager's subtree. The latter is bad bad bad, but the the
+ // MOZ_ASSERT() above will flag it. Returning nullptr here will just
+ // cause the shadow subtree not to be rendered.
+ NS_WARNING("Remote iframe not rendered");
+ return nullptr;
+ }
+
+ if (!mLayersId.IsValid()) {
+ return nullptr;
+ }
+
+ if (RefPtr<RemoteBrowser> remoteBrowser =
+ GetFrameLoader()->GetRemoteBrowser()) {
+ // Adjust mItemVisibleRect, which is relative to the reference frame, to be
+ // relative to this frame
+ nsRect visibleRect;
+ if (aContainerParameters.mItemVisibleRect) {
+ visibleRect = *aContainerParameters.mItemVisibleRect;
+ } else {
+ visibleRect = GetBuildingRect();
+ }
+ visibleRect -= ToReferenceFrame();
+ nsRect contentRect = Frame()->GetContentRectRelativeToSelf();
+ visibleRect.IntersectRect(visibleRect, contentRect);
+ visibleRect -= contentRect.TopLeft();
+
+ // Generate an effects update notifying the browser it is visible
+ aBuilder->AddEffectUpdate(remoteBrowser,
+ EffectsInfo::VisibleWithinRect(
+ visibleRect, aContainerParameters.mXScale,
+ aContainerParameters.mYScale));
+ // FrameLayerBuilder will take care of notifying the browser when it is no
+ // longer visible
+ }
+
+ RefPtr<Layer> layer =
+ aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this);
+
+ if (!layer) {
+ layer = aManager->CreateRefLayer();
+ }
+ if (!layer || !layer->AsRefLayer()) {
+ // Probably a temporary layer manager that doesn't know how to
+ // use ref layers.
+ return nullptr;
+ }
+ RefLayer* refLayer = layer->AsRefLayer();
+
+ nsPoint layerOffset = GetContentRectLayerOffset(Frame(), aBuilder);
+ nscoord auPerDevPixel = Frame()->PresContext()->AppUnitsPerDevPixel();
+ LayoutDeviceIntPoint offset =
+ mozilla::LayoutDeviceIntPoint::FromAppUnitsToNearest(layerOffset,
+ auPerDevPixel);
+
+ // We can only have an offset if we're a child of an inactive
+ // container, but our display item is LAYER_ACTIVE_FORCE which
+ // forces all layers above to be active.
+ MOZ_ASSERT(aContainerParameters.mOffset == nsIntPoint());
+ Matrix4x4 m = Matrix4x4::Translation(offset.x, offset.y, 0.0);
+ // Remote content can't be repainted by us, so we multiply down
+ // the resolution that our container expects onto our container.
+ m.PostScale(aContainerParameters.mXScale, aContainerParameters.mYScale, 1.0);
+ refLayer->SetBaseTransform(m);
+ refLayer->SetEventRegionsOverride(mEventRegionsOverride);
+ refLayer->SetReferentId(mLayersId);
+ refLayer->SetRemoteDocumentSize(GetFrameSize(mFrame));
+
+ return layer.forget();
+}
+
+void nsDisplayRemote::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
+ nsPresContext* pc = mFrame->PresContext();
+ nsFrameLoader* fl = GetFrameLoader();
+ if (pc->GetPrintSettings() && fl->IsRemoteFrame()) {
+ // See the comment below in CreateWebRenderCommands() as for why doing this.
+ fl->UpdatePositionAndSize(static_cast<nsSubDocumentFrame*>(mFrame));
+ }
+
+ DrawTarget* target = aCtx->GetDrawTarget();
+ if (!target->IsRecording() || mTabId == 0) {
+ NS_WARNING("Remote iframe not rendered");
+ return;
+ }
+
+ int32_t appUnitsPerDevPixel = pc->AppUnitsPerDevPixel();
+ Rect destRect =
+ NSRectToSnappedRect(GetContentRect(), appUnitsPerDevPixel, *target);
+ target->DrawDependentSurface(mTabId, destRect);
+}
+
+bool nsDisplayRemote::CreateWebRenderCommands(
+ mozilla::wr::DisplayListBuilder& aBuilder,
+ mozilla::wr::IpcResourceUpdateQueue& aResources,
+ const StackingContextHelper& aSc,
+ mozilla::layers::RenderRootStateManager* aManager,
+ nsDisplayListBuilder* aDisplayListBuilder) {
+ if (!mLayersId.IsValid()) {
+ return true;
+ }
+
+ nsPresContext* pc = mFrame->PresContext();
+ nsFrameLoader* fl = GetFrameLoader();
+ if (RefPtr<RemoteBrowser> remoteBrowser = fl->GetRemoteBrowser()) {
+ if (pc->GetPrintSettings()) {
+ // HACK(emilio): Usually we update sizing/positioning from
+ // ReflowFinished(). Print documents have no incremental reflow at all
+ // though, so we can't rely on it firing after a frame becomes remote.
+ // Thus, if we're painting a remote frame, update its sizing and position
+ // now.
+ //
+ // UpdatePositionAndSize() can cause havoc for non-remote frames but
+ // luckily we don't care about those, so this is fine.
+ fl->UpdatePositionAndSize(static_cast<nsSubDocumentFrame*>(mFrame));
+ }
+
+ // Adjust mItemVisibleRect, which is relative to the reference frame, to be
+ // relative to this frame
+ nsRect visibleRect = GetBuildingRect() - ToReferenceFrame();
+ nsRect contentRect = Frame()->GetContentRectRelativeToSelf();
+ visibleRect.IntersectRect(visibleRect, contentRect);
+ visibleRect -= contentRect.TopLeft();
+
+ // Generate an effects update notifying the browser it is visible
+ // TODO - Gather scaling factors
+ aDisplayListBuilder->AddEffectUpdate(
+ remoteBrowser, EffectsInfo::VisibleWithinRect(visibleRect, 1.0f, 1.0f));
+
+ // Create a WebRenderRemoteData to notify the RemoteBrowser when it is no
+ // longer visible
+ RefPtr<WebRenderRemoteData> userData =
+ aManager->CommandBuilder()
+ .CreateOrRecycleWebRenderUserData<WebRenderRemoteData>(this,
+ nullptr);
+ userData->SetRemoteBrowser(remoteBrowser);
+ }
+
+ nscoord auPerDevPixel = pc->AppUnitsPerDevPixel();
+ nsPoint layerOffset = GetContentRectLayerOffset(mFrame, aDisplayListBuilder);
+ mOffset = LayoutDevicePoint::FromAppUnits(layerOffset, auPerDevPixel);
+
+ nsRect contentRect = mFrame->GetContentRectRelativeToSelf();
+ contentRect.MoveTo(0, 0);
+ LayoutDeviceRect rect =
+ LayoutDeviceRect::FromAppUnits(contentRect, auPerDevPixel);
+ rect += mOffset;
+
+ aBuilder.PushIFrame(mozilla::wr::ToLayoutRect(rect), !BackfaceIsHidden(),
+ mozilla::wr::AsPipelineId(mLayersId),
+ /*ignoreMissingPipelines*/ true);
+
+ return true;
+}
+
+bool nsDisplayRemote::UpdateScrollData(
+ mozilla::layers::WebRenderScrollData* aData,
+ mozilla::layers::WebRenderLayerScrollData* aLayerData) {
+ if (!mLayersId.IsValid()) {
+ return true;
+ }
+
+ if (aLayerData) {
+ aLayerData->SetReferentId(mLayersId);
+ Matrix4x4 m = Matrix4x4::Translation(mOffset.x, mOffset.y, 0.0);
+
+ // Apply the top level resolution if we are in the same process of the top
+ // level document. We don't need to apply it in cases where we are in OOP
+ // iframes since it will be applied later in
+ // HitTestingTreeNode::GetTransformToGecko by walking up the tree node.
+ nsPresContext* inProcessRootContext =
+ mFrame->PresContext()->GetInProcessRootContentDocumentPresContext();
+ if (inProcessRootContext &&
+ inProcessRootContext->IsRootContentDocumentCrossProcess()) {
+ float resolution = inProcessRootContext->PresShell()->GetResolution();
+ m.PostScale(resolution, resolution, 1.0);
+ }
+
+ aLayerData->SetTransform(m);
+ aLayerData->SetEventRegionsOverride(mEventRegionsOverride);
+ aLayerData->SetRemoteDocumentSize(GetFrameSize(mFrame));
+ }
+ return true;
+}
+
+nsFrameLoader* nsDisplayRemote::GetFrameLoader() const {
+ return mFrame ? static_cast<nsSubDocumentFrame*>(mFrame)->FrameLoader()
+ : nullptr;
+}