fune/layout/generic/nsSubDocumentFrame.cpp
Boris Chiou dd3c20ccd8 Bug 1639963 - Update the API of nsIFrame::ComputeSize for aspect-ratio. r=emilio
In order to apply Automatic content-based minimum sizes, we have to know
the content size on the block axis. We cannot get the content size until
we finish the reflow of the child frames. So we have to keep a flag
which indicates the size of the ratio-dependent axis is overrideen by
aspect-ratio in ReflowInput.

We will set the correct return value in the next patch, For now, we
always return AspectRatioUsage::None.

Differential Revision: https://phabricator.services.mozilla.com/D79335
2020-08-17 06:57:56 +00:00

1418 lines
51 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/. */
/*
* 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 "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;
}
if (!PresContext()->IsDynamic()) {
// We let the printing code take care of loading the document; just
// create the inner view for it to use.
(void)EnsureInnerView();
} else {
RefPtr<nsFrameLoader> frameloader = FrameLoader();
if (frameloader) {
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);
if (nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame()) {
result = subDocRoot->GetMinISize(aRenderingContext);
} else {
result = GetIntrinsicISize();
}
return result;
}
/* virtual */
nscoord nsSubDocumentFrame::GetPrefISize(gfxContext* aRenderingContext) {
nscoord result;
DISPLAY_PREF_INLINE_SIZE(this, result);
nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
if (subDocRoot) {
result = subDocRoot->GetPrefISize(aRenderingContext);
} else {
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 (nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame()) {
return subDocRoot->GetIntrinsicSize();
}
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>. Default to size of 300px x 150px, for IE
// compat (and per CSS2.1 draft)
return IntrinsicSize(CSSPixel::ToAppUnits(300), CSSPixel::ToAppUnits(150));
}
/* virtual */
AspectRatio nsSubDocumentFrame::GetIntrinsicRatio() {
// 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 (nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame()) {
return subDocRoot->GetIntrinsicRatio();
}
// 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& aBorder, const LogicalSize& aPadding,
ComputeSizeFlags aFlags) {
if (!IsInline()) {
return nsIFrame::ComputeAutoSize(aRenderingContext, aWM, aCBSize,
aAvailableISize, aMargin, aBorder,
aPadding, 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& aBorder, const LogicalSize& aPadding,
ComputeSizeFlags aFlags) {
return {ComputeSizeWithIntrinsicDimensions(
aRenderingContext, aWM, GetIntrinsicSize(), GetIntrinsicRatio(),
aCBSize, aMargin, aBorder, aPadding, 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>
aDesiredSize.SetSize(aReflowInput.GetWritingMode(),
aReflowInput.ComputedSizeWithBorderPadding());
// "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 {
nsIContent* content = GetContent();
if (!content) return nullptr;
if (!mFrameLoader) {
RefPtr<nsFrameLoaderOwner> loaderOwner = do_QueryObject(content);
if (loaderOwner) {
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() {
// 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();
}
nsIFrame* nsSubDocumentFrame::ObtainIntrinsicSizeFrame() {
if (StyleDisplay()->IsContainSize()) {
// Intrinsic size of 'contain:size' replaced elements is 0,0. So, don't use
// internal frames to provide intrinsic size at all.
return nullptr;
}
nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(GetContent());
if (olc) {
// We are an HTML <object> or <embed> (a replaced element).
// Try to get an nsIFrame for our sub-document's document element
nsIFrame* subDocRoot = nullptr;
if (nsIDocShell* docShell = GetDocShell()) {
if (mozilla::PresShell* presShell = docShell->GetPresShell()) {
nsIScrollableFrame* scrollable =
presShell->GetRootScrollFrameAsScrollable();
if (scrollable) {
nsIFrame* scrolled = scrollable->GetScrolledFrame();
if (scrolled) {
subDocRoot = scrolled->PrincipalChildList().FirstChild();
}
}
}
}
if (subDocRoot && subDocRoot->GetContent() &&
subDocRoot->GetContent()->NodeInfo()->Equals(nsGkAtoms::svg,
kNameSpaceID_SVG)) {
return subDocRoot; // SVG documents have an intrinsic size
}
}
return nullptr;
}
/**
* 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.PreScale(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) {
DrawTarget* target = aCtx->GetDrawTarget();
if (!target->IsRecording() || mTabId == 0) {
NS_WARNING("Remote iframe not rendered");
return;
}
int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
Rect destRect = mozilla::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;
}
if (RefPtr<RemoteBrowser> remoteBrowser =
GetFrameLoader()->GetRemoteBrowser()) {
// 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 = mFrame->PresContext()->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);
aLayerData->SetTransform(
mozilla::gfx::Matrix4x4::Translation(mOffset.x, mOffset.y, 0.0));
aLayerData->SetEventRegionsOverride(mEventRegionsOverride);
aLayerData->SetRemoteDocumentSize(GetFrameSize(mFrame));
}
return true;
}
nsFrameLoader* nsDisplayRemote::GetFrameLoader() const {
return mFrame ? static_cast<nsSubDocumentFrame*>(mFrame)->FrameLoader()
: nullptr;
}