1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 * rendering object for replaced elements that contain a document, such
9 * as <frame>, <iframe>, and some <object>s
12 #include "nsSubDocumentFrame.h"
14 #include "mozilla/Preferences.h"
15 #include "mozilla/PresShell.h"
16 #include "mozilla/StaticPrefs_layout.h"
17 #include "mozilla/Unused.h"
18 #include "mozilla/dom/Document.h"
19 #include "mozilla/dom/HTMLFrameElement.h"
20 #include "mozilla/dom/BrowserParent.h"
23 #include "nsGenericHTMLElement.h"
24 #include "nsGenericHTMLFrameElement.h"
25 #include "nsAttrValueInlines.h"
26 #include "nsIDocShell.h"
27 #include "nsIContentViewer.h"
28 #include "nsIContentInlines.h"
29 #include "nsPresContext.h"
31 #include "nsViewManager.h"
32 #include "nsGkAtoms.h"
33 #include "nsStyleConsts.h"
34 #include "nsStyleStruct.h"
35 #include "nsStyleStructInlines.h"
36 #include "nsFrameSetFrame.h"
37 #include "nsNameSpaceManager.h"
38 #include "nsDisplayList.h"
39 #include "nsIScrollableFrame.h"
40 #include "nsIObjectLoadingContent.h"
41 #include "nsLayoutUtils.h"
42 #include "FrameLayerBuilder.h"
43 #include "nsContentUtils.h"
44 #include "nsServiceManagerUtils.h"
45 #include "nsQueryObject.h"
46 #include "RetainedDisplayListBuilder.h"
47 #include "nsObjectLoadingContent.h"
50 #include "BasicLayers.h"
51 #include "mozilla/layers/WebRenderUserData.h"
52 #include "mozilla/layers/WebRenderScrollData.h"
53 #include "mozilla/layers/RenderRootStateManager.h"
54 #include "mozilla/ProfilerLabels.h"
56 using namespace mozilla
;
57 using namespace mozilla::dom
;
58 using namespace mozilla::gfx
;
59 using namespace mozilla::layers
;
61 static Document
* GetDocumentFromView(nsView
* aView
) {
62 MOZ_ASSERT(aView
, "null view");
64 nsViewManager
* vm
= aView
->GetViewManager();
65 PresShell
* presShell
= vm
? vm
->GetPresShell() : nullptr;
66 return presShell
? presShell
->GetDocument() : nullptr;
69 nsSubDocumentFrame::nsSubDocumentFrame(ComputedStyle
* aStyle
,
70 nsPresContext
* aPresContext
)
71 : nsAtomicContainerFrame(aStyle
, aPresContext
, kClassID
),
75 mPostedReflowCallback(false),
77 mCallingShow(false) {}
80 a11y::AccType
nsSubDocumentFrame::AccessibleType() {
81 return a11y::eOuterDocType
;
85 NS_QUERYFRAME_HEAD(nsSubDocumentFrame
)
86 NS_QUERYFRAME_ENTRY(nsSubDocumentFrame
)
87 NS_QUERYFRAME_TAIL_INHERITING(nsAtomicContainerFrame
)
89 class AsyncFrameInit
: public Runnable
{
91 explicit AsyncFrameInit(nsIFrame
* aFrame
)
92 : mozilla::Runnable("AsyncFrameInit"), mFrame(aFrame
) {}
93 NS_IMETHOD
Run() override
{
94 AUTO_PROFILER_LABEL("AsyncFrameInit::Run", OTHER
);
95 if (mFrame
.IsAlive()) {
96 static_cast<nsSubDocumentFrame
*>(mFrame
.GetFrame())->ShowViewer();
105 static void InsertViewsInReverseOrder(nsView
* aSibling
, nsView
* aParent
);
107 static void EndSwapDocShellsForViews(nsView
* aView
);
109 void nsSubDocumentFrame::Init(nsIContent
* aContent
, nsContainerFrame
* aParent
,
110 nsIFrame
* aPrevInFlow
) {
111 MOZ_ASSERT(aContent
);
112 // determine if we are a <frame> or <iframe>
113 mIsInline
= !aContent
->IsHTMLElement(nsGkAtoms::frame
);
115 nsAtomicContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
117 // CreateView() creates this frame's view, stored in mOuterView. It needs to
118 // be created first since it's the parent of the inner view, stored in
123 // Set the primary frame now so that nsDocumentViewer::FindContainerView
124 // called from within EndSwapDocShellsForViews below can find it if needed.
125 aContent
->SetPrimaryFrame(this);
127 // If we have a detached subdoc's root view on our frame loader, re-insert
128 // it into the view tree. This happens when we've been reframed, and
129 // ensures the presentation persists across reframes. If the frame element
130 // has changed documents however, we blow away the presentation.
131 RefPtr
<nsFrameLoader
> frameloader
= FrameLoader();
133 nsCOMPtr
<Document
> oldContainerDoc
;
134 nsIFrame
* detachedFrame
=
135 frameloader
->GetDetachedSubdocFrame(getter_AddRefs(oldContainerDoc
));
136 frameloader
->SetDetachedSubdocFrame(nullptr, nullptr);
137 MOZ_ASSERT(oldContainerDoc
|| !detachedFrame
);
138 if (oldContainerDoc
) {
139 nsView
* detachedView
= detachedFrame
? detachedFrame
->GetView() : nullptr;
140 if (detachedView
&& oldContainerDoc
== aContent
->OwnerDoc()) {
141 // Restore stashed presentation.
142 ::InsertViewsInReverseOrder(detachedView
, mInnerView
);
143 ::EndSwapDocShellsForViews(mInnerView
->GetFirstChild());
145 // Presentation is for a different document, don't restore it.
151 PropagateIsUnderHiddenEmbedderElementToSubView(
152 PresShell()->IsUnderHiddenEmbedderElement() ||
153 !StyleVisibility()->IsVisible());
155 nsContentUtils::AddScriptRunner(new AsyncFrameInit(this));
158 void nsSubDocumentFrame::PropagateIsUnderHiddenEmbedderElementToSubView(
159 bool aIsUnderHiddenEmbedderElement
) {
160 if (mFrameLoader
&& mFrameLoader
->IsRemoteFrame()) {
161 mFrameLoader
->SendIsUnderHiddenEmbedderElement(
162 aIsUnderHiddenEmbedderElement
);
170 nsView
* subdocView
= mInnerView
->GetFirstChild();
172 if (mozilla::PresShell
* presShell
= subdocView
->GetPresShell()) {
173 presShell
->SetIsUnderHiddenEmbedderElement(aIsUnderHiddenEmbedderElement
);
175 subdocView
= subdocView
->GetNextSibling();
179 void nsSubDocumentFrame::ShowViewer() {
184 RefPtr
<nsFrameLoader
> frameloader
= FrameLoader();
189 if (!frameloader
->IsRemoteFrame() && !PresContext()->IsDynamic()) {
190 // We let the printing code take care of loading the document and
191 // initializing the shell; just create the inner view for it to use.
192 (void)EnsureInnerView();
194 AutoWeakFrame
weakThis(this);
196 bool didCreateDoc
= frameloader
->Show(this);
197 if (!weakThis
.IsAlive()) {
200 mCallingShow
= false;
201 mDidCreateDoc
= didCreateDoc
;
203 if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW
)) {
204 frameloader
->UpdatePositionAndSize(this);
207 if (!weakThis
.IsAlive()) {
214 nsIFrame
* nsSubDocumentFrame::GetSubdocumentRootFrame() {
215 if (!mInnerView
) return nullptr;
216 nsView
* subdocView
= mInnerView
->GetFirstChild();
217 return subdocView
? subdocView
->GetFrame() : nullptr;
220 mozilla::PresShell
* nsSubDocumentFrame::GetSubdocumentPresShellForPainting(
222 if (!mInnerView
) return nullptr;
224 nsView
* subdocView
= mInnerView
->GetFirstChild();
225 if (!subdocView
) return nullptr;
227 mozilla::PresShell
* presShell
= nullptr;
229 nsIFrame
* subdocRootFrame
= subdocView
->GetFrame();
230 if (subdocRootFrame
) {
231 presShell
= subdocRootFrame
->PresShell();
234 // If painting is suppressed in the presshell, we try to look for a better
236 if (!presShell
|| (presShell
->IsPaintingSuppressed() &&
237 !(aFlags
& IGNORE_PAINT_SUPPRESSION
))) {
238 // During page transition mInnerView will sometimes have two children, the
239 // first being the new page that may not have any frame, and the second
240 // being the old page that will probably have a frame.
241 nsView
* nextView
= subdocView
->GetNextSibling();
242 nsIFrame
* frame
= nullptr;
244 frame
= nextView
->GetFrame();
247 mozilla::PresShell
* presShellForNextView
= frame
->PresShell();
248 if (!presShell
|| (presShellForNextView
&&
249 !presShellForNextView
->IsPaintingSuppressed() &&
250 StaticPrefs::layout_show_previous_page())) {
251 subdocView
= nextView
;
252 subdocRootFrame
= frame
;
253 presShell
= presShellForNextView
;
257 // If we don't have a frame we use this roundabout way to get the pres
259 if (!mFrameLoader
) return nullptr;
260 nsIDocShell
* docShell
= mFrameLoader
->GetDocShell(IgnoreErrors());
261 if (!docShell
) return nullptr;
262 presShell
= docShell
->GetPresShell();
269 ScreenIntSize
nsSubDocumentFrame::GetSubdocumentSize() {
270 if (HasAnyStateBits(NS_FRAME_FIRST_REFLOW
)) {
271 if (RefPtr
<nsFrameLoader
> frameloader
= FrameLoader()) {
272 nsCOMPtr
<Document
> oldContainerDoc
;
273 nsIFrame
* detachedFrame
=
274 frameloader
->GetDetachedSubdocFrame(getter_AddRefs(oldContainerDoc
));
275 if (nsView
* view
= detachedFrame
? detachedFrame
->GetView() : nullptr) {
276 nsSize size
= view
->GetBounds().Size();
277 nsPresContext
* presContext
= detachedFrame
->PresContext();
278 return ScreenIntSize(presContext
->AppUnitsToDevPixels(size
.width
),
279 presContext
->AppUnitsToDevPixels(size
.height
));
282 // Pick some default size for now. Using 10x10 because that's what the
284 return ScreenIntSize(10, 10);
287 nsSize docSizeAppUnits
;
288 nsPresContext
* presContext
= PresContext();
289 if (GetContent()->IsHTMLElement(nsGkAtoms::frame
)) {
290 docSizeAppUnits
= GetSize();
292 docSizeAppUnits
= GetContentRect().Size();
295 // Adjust subdocument size, according to 'object-fit' and the subdocument's
296 // intrinsic size and ratio.
297 docSizeAppUnits
= nsLayoutUtils::ComputeObjectDestRect(
298 nsRect(nsPoint(), docSizeAppUnits
), GetIntrinsicSize(),
299 GetIntrinsicRatio(), StylePosition())
302 return ScreenIntSize(
303 presContext
->AppUnitsToDevPixels(docSizeAppUnits
.width
),
304 presContext
->AppUnitsToDevPixels(docSizeAppUnits
.height
));
307 static void WrapBackgroundColorInOwnLayer(nsDisplayListBuilder
* aBuilder
,
309 nsDisplayList
* aList
) {
310 nsDisplayList tempItems
;
312 while ((item
= aList
->RemoveBottom()) != nullptr) {
313 if (item
->GetType() == DisplayItemType::TYPE_BACKGROUND_COLOR
) {
314 nsDisplayList tmpList
;
315 tmpList
.AppendToTop(item
);
316 item
= MakeDisplayItemWithIndex
<nsDisplayOwnLayer
>(
317 aBuilder
, aFrame
, /* aIndex = */ nsDisplayOwnLayer::OwnLayerForSubdoc
,
318 &tmpList
, aBuilder
->CurrentActiveScrolledRoot(),
319 nsDisplayOwnLayerFlags::None
, ScrollbarData
{}, true, false);
322 tempItems
.AppendToTop(item
);
325 aList
->AppendToTop(&tempItems
);
328 void nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
329 const nsDisplayListSet
& aLists
) {
330 if (!IsVisibleForPainting()) {
334 nsFrameLoader
* frameLoader
= FrameLoader();
335 bool isRemoteFrame
= frameLoader
&& frameLoader
->IsRemoteFrame();
337 // If we are pointer-events:none then we don't need to HitTest background
338 bool pointerEventsNone
=
339 StyleUI()->mPointerEvents
== StylePointerEvents::None
;
340 if (!aBuilder
->IsForEventDelivery() || !pointerEventsNone
) {
341 nsDisplayListCollection
decorations(aBuilder
);
342 DisplayBorderBackgroundOutline(aBuilder
, decorations
);
344 // Wrap background colors of <iframe>s with remote subdocuments in their
345 // own layer so we generate a ColorLayer. This is helpful for optimizing
346 // compositing; we can skip compositing the ColorLayer when the
347 // remote content is opaque.
348 WrapBackgroundColorInOwnLayer(aBuilder
, this,
349 decorations
.BorderBackground());
351 decorations
.MoveTo(aLists
);
354 if (aBuilder
->IsForEventDelivery() && pointerEventsNone
) {
358 // If we're passing pointer events to children then we have to descend into
359 // subdocuments no matter what, to determine which parts are transparent for
360 // hit-testing or event regions.
361 bool needToDescend
= aBuilder
->GetDescendIntoSubdocuments();
362 if (!mInnerView
|| !needToDescend
) {
367 // We're the subdoc for <browser remote="true"> and it has
368 // painted content. Display its shadow layer tree.
369 DisplayListClipState::AutoSaveRestore
clipState(aBuilder
);
370 clipState
.ClipContainingBlockDescendantsToContentBox(aBuilder
, this);
372 aLists
.Content()->AppendNewToTop
<nsDisplayRemote
>(aBuilder
, this);
376 RefPtr
<mozilla::PresShell
> presShell
= GetSubdocumentPresShellForPainting(
377 aBuilder
->IsIgnoringPaintSuppression() ? IGNORE_PAINT_SUPPRESSION
: 0);
383 if (aBuilder
->IsInFilter()) {
384 Document
* outerDoc
= PresShell()->GetDocument();
385 Document
* innerDoc
= presShell
->GetDocument();
386 if (outerDoc
&& innerDoc
) {
387 if (!outerDoc
->NodePrincipal()->Equals(innerDoc
->NodePrincipal())) {
388 outerDoc
->SetUseCounter(eUseCounter_custom_FilteredCrossOriginIFrame
);
393 nsIFrame
* subdocRootFrame
= presShell
->GetRootFrame();
395 nsPresContext
* presContext
= presShell
->GetPresContext();
397 int32_t parentAPD
= PresContext()->AppUnitsPerDevPixel();
398 int32_t subdocAPD
= presContext
->AppUnitsPerDevPixel();
402 bool ignoreViewportScrolling
= false;
403 if (subdocRootFrame
) {
404 // get the dirty rect relative to the root frame of the subdoc
405 visible
= aBuilder
->GetVisibleRect() + GetOffsetToCrossDoc(subdocRootFrame
);
406 dirty
= aBuilder
->GetDirtyRect() + GetOffsetToCrossDoc(subdocRootFrame
);
407 // and convert into the appunits of the subdoc
408 visible
= visible
.ScaleToOtherAppUnitsRoundOut(parentAPD
, subdocAPD
);
409 dirty
= dirty
.ScaleToOtherAppUnitsRoundOut(parentAPD
, subdocAPD
);
411 if (nsIScrollableFrame
* rootScrollableFrame
=
412 presShell
->GetRootScrollFrameAsScrollable()) {
413 // Use a copy, so the rects don't get modified.
414 nsRect copyOfDirty
= dirty
;
415 nsRect copyOfVisible
= visible
;
416 // TODO(botond): Can we just axe this DecideScrollableLayer call?
417 rootScrollableFrame
->DecideScrollableLayer(aBuilder
, ©OfVisible
,
419 /* aSetBase = */ true);
421 ignoreViewportScrolling
= presShell
->IgnoringViewportScrolling();
424 aBuilder
->EnterPresShell(subdocRootFrame
, pointerEventsNone
);
425 aBuilder
->IncrementPresShellPaintCount(presShell
);
427 visible
= aBuilder
->GetVisibleRect();
428 dirty
= aBuilder
->GetDirtyRect();
431 DisplayListClipState::AutoSaveRestore
clipState(aBuilder
);
432 clipState
.ClipContainingBlockDescendantsToContentBox(aBuilder
, this);
434 nsIScrollableFrame
* sf
= presShell
->GetRootScrollFrameAsScrollable();
435 bool constructZoomItem
= subdocRootFrame
&& parentAPD
!= subdocAPD
;
436 bool needsOwnLayer
= false;
437 if (constructZoomItem
|| presContext
->IsRootContentDocument() ||
438 (sf
&& sf
->IsScrollingActive(aBuilder
))) {
439 needsOwnLayer
= true;
442 nsDisplayList childItems
;
445 DisplayListClipState::AutoSaveRestore
nestedClipState(aBuilder
);
447 // Clear current clip. There's no point in propagating it down, since
448 // the layer we will construct will be clipped by the current clip.
449 // In fact for nsDisplayZoom propagating it down would be incorrect since
450 // nsDisplayZoom changes the meaning of appunits.
451 nestedClipState
.Clear();
454 // Invoke AutoBuildingDisplayList to ensure that the correct dirty rect
455 // is used to compute the visible rect if AddCanvasBackgroundColorItem
456 // creates a display item.
457 nsIFrame
* frame
= subdocRootFrame
? subdocRootFrame
: this;
458 nsDisplayListBuilder::AutoBuildingDisplayList
building(aBuilder
, frame
,
461 if (subdocRootFrame
) {
462 nsIFrame
* rootScrollFrame
= presShell
->GetRootScrollFrame();
463 nsDisplayListBuilder::AutoCurrentScrollParentIdSetter
idSetter(
465 ignoreViewportScrolling
&& rootScrollFrame
&&
466 rootScrollFrame
->GetContent()
467 ? nsLayoutUtils::FindOrCreateIDFor(rootScrollFrame
->GetContent())
468 : aBuilder
->GetCurrentScrollParentId());
470 bool hasDocumentLevelListenersForApzAwareEvents
=
471 gfxPlatform::AsyncPanZoomEnabled() &&
472 nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(presShell
);
474 aBuilder
->SetAncestorHasApzAwareEventHandler(
475 hasDocumentLevelListenersForApzAwareEvents
);
476 subdocRootFrame
->BuildDisplayListForStackingContext(aBuilder
,
480 if (!aBuilder
->IsForEventDelivery()) {
481 // If we are going to use a displayzoom below then any items we put under
482 // it need to have underlying frames from the subdocument. So we need to
483 // calculate the bounds based on which frame will be the underlying frame
484 // for the canvas background color item.
486 GetContentRectRelativeToSelf() + aBuilder
->ToReferenceFrame(this);
487 if (subdocRootFrame
) {
488 bounds
= bounds
.ScaleToOtherAppUnitsRoundOut(parentAPD
, subdocAPD
);
491 // If we are in print preview/page layout we want to paint the grey
492 // background behind the page, not the canvas color. The canvas color gets
493 // painted on the page itself.
494 if (nsLayoutUtils::NeedsPrintPreviewBackground(presContext
)) {
495 presShell
->AddPrintPreviewBackgroundItem(aBuilder
, &childItems
, frame
,
498 // Add the canvas background color to the bottom of the list. This
499 // happens after we've built the list so that
500 // AddCanvasBackgroundColorItem can monkey with the contents if
502 AddCanvasBackgroundColorFlags flags
=
503 AddCanvasBackgroundColorFlags::ForceDraw
|
504 AddCanvasBackgroundColorFlags::AddForSubDocument
;
505 presShell
->AddCanvasBackgroundColorItem(
506 aBuilder
, &childItems
, frame
, bounds
, NS_RGBA(0, 0, 0, 0), flags
);
511 if (subdocRootFrame
) {
512 aBuilder
->LeavePresShell(subdocRootFrame
, &childItems
);
515 // Generate a resolution and/or zoom item if needed. If one or both of those
516 // is created, we don't need to create a separate nsDisplaySubDocument.
518 nsDisplayOwnLayerFlags flags
=
519 nsDisplayOwnLayerFlags::GenerateSubdocInvalidations
;
520 // If ignoreViewportScrolling is true then the top most layer we create here
521 // is going to become the scrollable layer for the root scroll frame, so we
522 // want to add nsDisplayOwnLayer::GENERATE_SCROLLABLE_LAYER to whatever layer
523 // becomes the topmost. We do this below.
524 if (constructZoomItem
) {
525 nsDisplayOwnLayerFlags zoomFlags
= flags
;
526 if (ignoreViewportScrolling
) {
527 zoomFlags
|= nsDisplayOwnLayerFlags::GenerateScrollableLayer
;
529 childItems
.AppendNewToTop
<nsDisplayZoom
>(aBuilder
, subdocRootFrame
, this,
530 &childItems
, subdocAPD
, parentAPD
,
533 needsOwnLayer
= false;
535 // Wrap the zoom item in the resolution item if we have both because we want
536 // the resolution scale applied on top of the app units per dev pixel
538 if (ignoreViewportScrolling
) {
539 flags
|= nsDisplayOwnLayerFlags::GenerateScrollableLayer
;
542 // We always want top level content documents to be in their own layer.
543 nsDisplaySubDocument
* layerItem
= MakeDisplayItem
<nsDisplaySubDocument
>(
544 aBuilder
, subdocRootFrame
? subdocRootFrame
: this, this, &childItems
,
547 childItems
.AppendToTop(layerItem
);
548 layerItem
->SetShouldFlattenAway(!needsOwnLayer
);
551 if (aBuilder
->IsForFrameVisibility()) {
552 // We don't add the childItems to the return list as we're dealing with them
554 presShell
->RebuildApproximateFrameVisibilityDisplayList(childItems
);
555 childItems
.DeleteAll(aBuilder
);
557 aLists
.Content()->AppendToTop(&childItems
);
561 #ifdef DEBUG_FRAME_DUMP
562 void nsSubDocumentFrame::List(FILE* out
, const char* aPrefix
,
563 ListFlags aFlags
) const {
565 ListGeneric(str
, aPrefix
, aFlags
);
566 fprintf_stderr(out
, "%s\n", str
.get());
568 if (aFlags
.contains(ListFlag::TraverseSubdocumentFrames
)) {
569 nsSubDocumentFrame
* f
= const_cast<nsSubDocumentFrame
*>(this);
570 nsIFrame
* subdocRootFrame
= f
->GetSubdocumentRootFrame();
571 if (subdocRootFrame
) {
572 nsCString
pfx(aPrefix
);
574 subdocRootFrame
->List(out
, pfx
.get(), aFlags
);
579 nsresult
nsSubDocumentFrame::GetFrameName(nsAString
& aResult
) const {
580 return MakeFrameName(u
"FrameOuter"_ns
, aResult
);
585 nscoord
nsSubDocumentFrame::GetMinISize(gfxContext
* aRenderingContext
) {
587 DISPLAY_MIN_INLINE_SIZE(this, result
);
589 nsCOMPtr
<nsIObjectLoadingContent
> iolc
= do_QueryInterface(mContent
);
590 auto olc
= static_cast<nsObjectLoadingContent
*>(iolc
.get());
592 if (olc
&& olc
->GetSubdocumentIntrinsicSize()) {
593 // The subdocument is an SVG document, so technically we should call
594 // SVGOuterSVGFrame::GetMinISize() on its root frame. That method always
595 // returns 0, though, so we can just do that & don't need to bother with
596 // the cross-doc communication.
599 result
= GetIntrinsicISize();
606 nscoord
nsSubDocumentFrame::GetPrefISize(gfxContext
* aRenderingContext
) {
608 DISPLAY_PREF_INLINE_SIZE(this, result
);
610 // If the subdocument is an SVG document, then in theory we want to return
611 // the same thing that SVGOuterSVGFrame::GetPrefISize does. That method
612 // has some special handling of percentage values to avoid unhelpful zero
613 // sizing in the presence of orthogonal writing modes. We don't bother
614 // with that for SVG documents in <embed> and <object>, since that special
615 // handling doesn't look up across document boundaries anyway.
616 result
= GetIntrinsicISize();
622 IntrinsicSize
nsSubDocumentFrame::GetIntrinsicSize() {
623 if (StyleDisplay()->IsContainSize()) {
624 // Intrinsic size of 'contain:size' replaced elements is 0,0.
625 return IntrinsicSize(0, 0);
628 if (nsCOMPtr
<nsIObjectLoadingContent
> iolc
= do_QueryInterface(mContent
)) {
629 auto olc
= static_cast<nsObjectLoadingContent
*>(iolc
.get());
631 if (auto size
= olc
->GetSubdocumentIntrinsicSize()) {
632 // Use the intrinsic size from the child SVG document, if available.
638 return {}; // <frame> elements have no useful intrinsic size.
641 if (mContent
->IsXULElement()) {
642 return {}; // XUL <iframe> and <browser> have no useful intrinsic size
645 // We must be an HTML <iframe>. Return fallback size.
646 return IntrinsicSize(kFallbackIntrinsicSize
);
650 AspectRatio
nsSubDocumentFrame::GetIntrinsicRatio() const {
651 // FIXME(emilio): This should probably respect contain: size and return no
652 // ratio in the case subDocRoot is non-null. Otherwise we do it by virtue of
653 // using a zero-size below and reusing GetIntrinsicSize().
654 if (nsCOMPtr
<nsIObjectLoadingContent
> iolc
= do_QueryInterface(mContent
)) {
655 auto olc
= static_cast<nsObjectLoadingContent
*>(iolc
.get());
657 auto ratio
= olc
->GetSubdocumentIntrinsicRatio();
658 if (ratio
&& *ratio
) {
659 // Use the intrinsic aspect ratio from the child SVG document, if
665 // NOTE(emilio): Even though we have an intrinsic size, we may not have an
666 // intrinsic ratio. For example `<iframe style="width: 100px">` should not
667 // shrink in the vertical axis to preserve the 300x150 ratio.
668 return nsAtomicContainerFrame::GetIntrinsicRatio();
672 LogicalSize
nsSubDocumentFrame::ComputeAutoSize(
673 gfxContext
* aRenderingContext
, WritingMode aWM
, const LogicalSize
& aCBSize
,
674 nscoord aAvailableISize
, const LogicalSize
& aMargin
,
675 const LogicalSize
& aBorderPadding
, const StyleSizeOverrides
& aSizeOverrides
,
676 ComputeSizeFlags aFlags
) {
678 return nsIFrame::ComputeAutoSize(aRenderingContext
, aWM
, aCBSize
,
679 aAvailableISize
, aMargin
, aBorderPadding
,
680 aSizeOverrides
, aFlags
);
683 const WritingMode wm
= GetWritingMode();
684 LogicalSize
result(wm
, GetIntrinsicISize(), GetIntrinsicBSize());
685 return result
.ConvertTo(aWM
, wm
);
689 nsIFrame::SizeComputationResult
nsSubDocumentFrame::ComputeSize(
690 gfxContext
* aRenderingContext
, WritingMode aWM
, const LogicalSize
& aCBSize
,
691 nscoord aAvailableISize
, const LogicalSize
& aMargin
,
692 const LogicalSize
& aBorderPadding
, const StyleSizeOverrides
& aSizeOverrides
,
693 ComputeSizeFlags aFlags
) {
694 return {ComputeSizeWithIntrinsicDimensions(
695 aRenderingContext
, aWM
, GetIntrinsicSize(), GetAspectRatio(),
696 aCBSize
, aMargin
, aBorderPadding
, aSizeOverrides
, aFlags
),
697 AspectRatioUsage::None
};
700 void nsSubDocumentFrame::Reflow(nsPresContext
* aPresContext
,
701 ReflowOutput
& aDesiredSize
,
702 const ReflowInput
& aReflowInput
,
703 nsReflowStatus
& aStatus
) {
705 DO_GLOBAL_REFLOW_COUNT("nsSubDocumentFrame");
706 DISPLAY_REFLOW(aPresContext
, this, aReflowInput
, aDesiredSize
, aStatus
);
707 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
709 NS_FRAME_TRACE_CALLS
,
710 ("enter nsSubDocumentFrame::Reflow: maxSize=%d,%d",
711 aReflowInput
.AvailableWidth(), aReflowInput
.AvailableHeight()));
713 NS_ASSERTION(aReflowInput
.ComputedWidth() != NS_UNCONSTRAINEDSIZE
,
714 "Shouldn't have unconstrained stuff here "
715 "thanks to the rules of reflow");
716 NS_ASSERTION(NS_UNCONSTRAINEDSIZE
!= aReflowInput
.ComputedHeight(),
717 "Shouldn't have unconstrained stuff here "
718 "thanks to ComputeAutoSize");
720 NS_ASSERTION(mContent
->GetPrimaryFrame() == this, "Shouldn't happen");
722 // XUL <iframe> or <browser>, or HTML <iframe>, <object> or <embed>
723 const auto wm
= aReflowInput
.GetWritingMode();
724 aDesiredSize
.SetSize(wm
, aReflowInput
.ComputedSizeWithBorderPadding(wm
));
726 // "offset" is the offset of our content area from our frame's
728 nsPoint offset
= nsPoint(aReflowInput
.ComputedPhysicalBorderPadding().left
,
729 aReflowInput
.ComputedPhysicalBorderPadding().top
);
732 const nsMargin
& bp
= aReflowInput
.ComputedPhysicalBorderPadding();
733 nsSize
innerSize(aDesiredSize
.Width() - bp
.LeftRight(),
734 aDesiredSize
.Height() - bp
.TopBottom());
736 // Size & position the view according to 'object-fit' & 'object-position'.
737 nsRect destRect
= nsLayoutUtils::ComputeObjectDestRect(
738 nsRect(offset
, innerSize
), GetIntrinsicSize(), GetIntrinsicRatio(),
741 nsViewManager
* vm
= mInnerView
->GetViewManager();
742 vm
->MoveViewTo(mInnerView
, destRect
.x
, destRect
.y
);
743 vm
->ResizeView(mInnerView
, nsRect(nsPoint(0, 0), destRect
.Size()), true);
746 aDesiredSize
.SetOverflowAreasToDesiredBounds();
748 FinishAndStoreOverflow(&aDesiredSize
);
750 if (!aPresContext
->IsPaginated() && !mPostedReflowCallback
) {
751 PresShell()->PostReflowCallback(this);
752 mPostedReflowCallback
= true;
756 NS_FRAME_TRACE_CALLS
,
757 ("exit nsSubDocumentFrame::Reflow: size=%d,%d status=%s",
758 aDesiredSize
.Width(), aDesiredSize
.Height(), ToString(aStatus
).c_str()));
760 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowInput
, aDesiredSize
);
763 bool nsSubDocumentFrame::ReflowFinished() {
764 RefPtr
<nsFrameLoader
> frameloader
= FrameLoader();
766 AutoWeakFrame
weakFrame(this);
768 frameloader
->UpdatePositionAndSize(this);
770 if (weakFrame
.IsAlive()) {
771 // Make sure that we can post a reflow callback in the future.
772 mPostedReflowCallback
= false;
775 mPostedReflowCallback
= false;
780 void nsSubDocumentFrame::ReflowCallbackCanceled() {
781 mPostedReflowCallback
= false;
784 nsresult
nsSubDocumentFrame::AttributeChanged(int32_t aNameSpaceID
,
787 if (aNameSpaceID
!= kNameSpaceID_None
) {
791 // If the noResize attribute changes, dis/allow frame to be resized
792 if (aAttribute
== nsGkAtoms::noresize
) {
793 // Note that we're not doing content type checks, but that's ok -- if
794 // they'd fail we will just end up with a null framesetFrame.
795 if (mContent
->GetParent()->IsHTMLElement(nsGkAtoms::frameset
)) {
796 nsIFrame
* parentFrame
= GetParent();
799 // There is no interface for nsHTMLFramesetFrame so QI'ing to
800 // concrete class, yay!
801 nsHTMLFramesetFrame
* framesetFrame
= do_QueryFrame(parentFrame
);
803 framesetFrame
->RecalculateBorderResize();
807 } else if (aAttribute
== nsGkAtoms::marginwidth
||
808 aAttribute
== nsGkAtoms::marginheight
) {
809 // Notify the frameloader
810 if (RefPtr
<nsFrameLoader
> frameloader
= FrameLoader()) {
811 frameloader
->MarginsChanged();
818 void nsSubDocumentFrame::DidSetComputedStyle(ComputedStyle
* aOldComputedStyle
) {
819 nsAtomicContainerFrame::DidSetComputedStyle(aOldComputedStyle
);
821 // If this presshell has invisible ancestors, we don't need to propagate the
822 // visibility style change to the subdocument since the subdocument should
823 // have already set the IsUnderHiddenEmbedderElement flag in
824 // nsSubDocumentFrame::Init.
825 if (PresShell()->IsUnderHiddenEmbedderElement()) {
829 const bool isVisible
= StyleVisibility()->IsVisible();
830 if (!aOldComputedStyle
||
831 isVisible
!= aOldComputedStyle
->StyleVisibility()->IsVisible()) {
832 PropagateIsUnderHiddenEmbedderElementToSubView(!isVisible
);
836 nsIFrame
* NS_NewSubDocumentFrame(PresShell
* aPresShell
, ComputedStyle
* aStyle
) {
837 return new (aPresShell
)
838 nsSubDocumentFrame(aStyle
, aPresShell
->GetPresContext());
841 NS_IMPL_FRAMEARENA_HELPERS(nsSubDocumentFrame
)
843 class nsHideViewer
: public Runnable
{
845 nsHideViewer(nsIContent
* aFrameElement
, nsFrameLoader
* aFrameLoader
,
846 PresShell
* aPresShell
, bool aHideViewerIfFrameless
)
847 : mozilla::Runnable("nsHideViewer"),
848 mFrameElement(aFrameElement
),
849 mFrameLoader(aFrameLoader
),
850 mPresShell(aPresShell
),
851 mHideViewerIfFrameless(aHideViewerIfFrameless
) {
852 NS_ASSERTION(mFrameElement
, "Must have a frame element");
853 NS_ASSERTION(mFrameLoader
, "Must have a frame loader");
854 NS_ASSERTION(mPresShell
, "Must have a presshell");
857 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD
Run() override
{
858 // Flush frames, to ensure any pending display:none changes are made.
859 // Note it can be unsafe to flush if we've destroyed the presentation
860 // for some other reason, like if we're shutting down.
862 // But avoid the flush if we know for sure we're away, like when we're out
863 // of the document already.
865 // FIXME(emilio): This could still be a perf footgun when removing lots of
866 // siblings where each of them cause the reframe of an ancestor which happen
867 // to contain a subdocument.
869 // We should find some way to avoid that!
870 if (!mPresShell
->IsDestroying() && mFrameElement
->IsInComposedDoc()) {
871 MOZ_KnownLive(mPresShell
)->FlushPendingNotifications(FlushType::Frames
);
874 // Either the frame has been constructed by now, or it never will be,
875 // either way we want to clear the stashed views.
876 mFrameLoader
->SetDetachedSubdocFrame(nullptr, nullptr);
878 nsSubDocumentFrame
* frame
= do_QueryFrame(mFrameElement
->GetPrimaryFrame());
879 if ((!frame
&& mHideViewerIfFrameless
) || mPresShell
->IsDestroying()) {
880 // Either the frame element has no nsIFrame or the presshell is being
881 // destroyed. Hide the nsFrameLoader, which destroys the presentation.
882 mFrameLoader
->Hide();
888 nsCOMPtr
<nsIContent
> mFrameElement
;
889 RefPtr
<nsFrameLoader
> mFrameLoader
;
890 RefPtr
<PresShell
> mPresShell
;
891 bool mHideViewerIfFrameless
;
894 static nsView
* BeginSwapDocShellsForViews(nsView
* aSibling
);
896 void nsSubDocumentFrame::DestroyFrom(nsIFrame
* aDestructRoot
,
897 PostDestroyData
& aPostDestroyData
) {
898 if (mPostedReflowCallback
) {
899 PresShell()->CancelReflowCallback(this);
900 mPostedReflowCallback
= false;
903 // Detach the subdocument's views and stash them in the frame loader.
904 // We can then reattach them if we're being reframed (for example if
905 // the frame has been made position:fixed).
906 RefPtr
<nsFrameLoader
> frameloader
= FrameLoader();
908 nsView
* detachedViews
=
909 ::BeginSwapDocShellsForViews(mInnerView
->GetFirstChild());
911 if (detachedViews
&& detachedViews
->GetFrame()) {
912 frameloader
->SetDetachedSubdocFrame(detachedViews
->GetFrame(),
913 mContent
->OwnerDoc());
915 // We call nsFrameLoader::HideViewer() in a script runner so that we can
916 // safely determine whether the frame is being reframed or destroyed.
917 nsContentUtils::AddScriptRunner(new nsHideViewer(
918 mContent
, frameloader
, PresShell(), (mDidCreateDoc
|| mCallingShow
)));
920 frameloader
->SetDetachedSubdocFrame(nullptr, nullptr);
921 if (mDidCreateDoc
|| mCallingShow
) {
927 nsAtomicContainerFrame::DestroyFrom(aDestructRoot
, aPostDestroyData
);
930 nsFrameLoader
* nsSubDocumentFrame::FrameLoader() const {
935 if (RefPtr
<nsFrameLoaderOwner
> loaderOwner
= do_QueryObject(GetContent())) {
936 mFrameLoader
= loaderOwner
->GetFrameLoader();
942 void nsSubDocumentFrame::ResetFrameLoader() {
943 mFrameLoader
= nullptr;
945 nsContentUtils::AddScriptRunner(new AsyncFrameInit(this));
948 // XXX this should be called ObtainDocShell or something like that,
949 // to indicate that it could have side effects
950 nsIDocShell
* nsSubDocumentFrame::GetDocShell() const {
951 // How can FrameLoader() return null???
952 if (NS_WARN_IF(!FrameLoader())) {
955 return mFrameLoader
->GetDocShell(IgnoreErrors());
958 static void DestroyDisplayItemDataForFrames(nsIFrame
* aFrame
) {
959 FrameLayerBuilder::DestroyDisplayItemDataFor(aFrame
);
961 for (const auto& childList
: aFrame
->ChildLists()) {
962 for (nsIFrame
* child
: childList
.mList
) {
963 DestroyDisplayItemDataForFrames(child
);
968 static CallState
BeginSwapDocShellsForDocument(Document
& aDocument
) {
969 if (PresShell
* presShell
= aDocument
.GetPresShell()) {
970 // Disable painting while the views are detached, see bug 946929.
971 presShell
->SetNeverPainting(true);
973 if (nsIFrame
* rootFrame
= presShell
->GetRootFrame()) {
974 ::DestroyDisplayItemDataForFrames(rootFrame
);
977 aDocument
.EnumerateSubDocuments(BeginSwapDocShellsForDocument
);
978 return CallState::Continue
;
981 static nsView
* BeginSwapDocShellsForViews(nsView
* aSibling
) {
982 // Collect the removed sibling views in reverse order in 'removedViews'.
983 nsView
* removedViews
= nullptr;
985 if (Document
* doc
= ::GetDocumentFromView(aSibling
)) {
986 ::BeginSwapDocShellsForDocument(*doc
);
988 nsView
* next
= aSibling
->GetNextSibling();
989 aSibling
->GetViewManager()->RemoveChild(aSibling
);
990 aSibling
->SetNextSibling(removedViews
);
991 removedViews
= aSibling
;
997 static void InsertViewsInReverseOrder(nsView
* aSibling
, nsView
* aParent
) {
998 MOZ_ASSERT(aParent
, "null view");
999 MOZ_ASSERT(!aParent
->GetFirstChild(), "inserting into non-empty list");
1001 nsViewManager
* vm
= aParent
->GetViewManager();
1003 nsView
* next
= aSibling
->GetNextSibling();
1004 aSibling
->SetNextSibling(nullptr);
1005 // true means 'after' in document order which is 'before' in view order,
1006 // so this call prepends the child, thus reversing the siblings as we go.
1007 vm
->InsertChild(aParent
, aSibling
, nullptr, true);
1012 nsresult
nsSubDocumentFrame::BeginSwapDocShells(nsIFrame
* aOther
) {
1013 if (!aOther
|| !aOther
->IsSubDocumentFrame()) {
1014 return NS_ERROR_NOT_IMPLEMENTED
;
1017 nsSubDocumentFrame
* other
= static_cast<nsSubDocumentFrame
*>(aOther
);
1018 if (!mFrameLoader
|| !mDidCreateDoc
|| mCallingShow
|| !other
->mFrameLoader
||
1019 !other
->mDidCreateDoc
) {
1020 return NS_ERROR_NOT_IMPLEMENTED
;
1023 ClearDisplayItems();
1024 other
->ClearDisplayItems();
1026 if (mInnerView
&& other
->mInnerView
) {
1027 nsView
* ourSubdocViews
= mInnerView
->GetFirstChild();
1028 nsView
* ourRemovedViews
= ::BeginSwapDocShellsForViews(ourSubdocViews
);
1029 nsView
* otherSubdocViews
= other
->mInnerView
->GetFirstChild();
1030 nsView
* otherRemovedViews
= ::BeginSwapDocShellsForViews(otherSubdocViews
);
1032 ::InsertViewsInReverseOrder(ourRemovedViews
, other
->mInnerView
);
1033 ::InsertViewsInReverseOrder(otherRemovedViews
, mInnerView
);
1035 mFrameLoader
.swap(other
->mFrameLoader
);
1039 static CallState
EndSwapDocShellsForDocument(Document
& aDocument
) {
1040 // Our docshell and view trees have been updated for the new hierarchy.
1041 // Now also update all nsDeviceContext::mWidget to that of the
1042 // container view in the new hierarchy.
1043 if (nsCOMPtr
<nsIDocShell
> ds
= aDocument
.GetDocShell()) {
1044 nsCOMPtr
<nsIContentViewer
> cv
;
1045 ds
->GetContentViewer(getter_AddRefs(cv
));
1047 RefPtr
<nsPresContext
> pc
= cv
->GetPresContext();
1048 if (pc
&& pc
->GetPresShell()) {
1049 pc
->GetPresShell()->SetNeverPainting(ds
->IsInvisible());
1051 nsDeviceContext
* dc
= pc
? pc
->DeviceContext() : nullptr;
1053 nsView
* v
= cv
->FindContainerView();
1054 dc
->Init(v
? v
->GetNearestWidget(nullptr) : nullptr);
1056 cv
= cv
->GetPreviousViewer();
1060 aDocument
.EnumerateSubDocuments(EndSwapDocShellsForDocument
);
1061 return CallState::Continue
;
1064 static void EndSwapDocShellsForViews(nsView
* aSibling
) {
1065 for (; aSibling
; aSibling
= aSibling
->GetNextSibling()) {
1066 if (Document
* doc
= ::GetDocumentFromView(aSibling
)) {
1067 ::EndSwapDocShellsForDocument(*doc
);
1069 nsIFrame
* frame
= aSibling
->GetFrame();
1071 nsIFrame
* parent
= nsLayoutUtils::GetCrossDocParentFrameInProcess(frame
);
1072 if (parent
->HasAnyStateBits(NS_FRAME_IN_POPUP
)) {
1073 nsIFrame::AddInPopupStateBitToDescendants(frame
);
1075 nsIFrame::RemoveInPopupStateBitFromDescendants(frame
);
1077 if (frame
->HasInvalidFrameInSubtree()) {
1079 !parent
->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT
|
1080 NS_FRAME_IS_NONDISPLAY
)) {
1081 parent
->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT
);
1082 parent
= nsLayoutUtils::GetCrossDocParentFrameInProcess(parent
);
1089 void nsSubDocumentFrame::EndSwapDocShells(nsIFrame
* aOther
) {
1090 nsSubDocumentFrame
* other
= static_cast<nsSubDocumentFrame
*>(aOther
);
1091 AutoWeakFrame
weakThis(this);
1092 AutoWeakFrame
weakOther(aOther
);
1095 ::EndSwapDocShellsForViews(mInnerView
->GetFirstChild());
1097 if (other
->mInnerView
) {
1098 ::EndSwapDocShellsForViews(other
->mInnerView
->GetFirstChild());
1101 // Now make sure we reflow both frames, in case their contents
1102 // determine their size.
1103 // And repaint them, for good measure, in case there's nothing
1104 // interesting that happens during reflow.
1105 if (weakThis
.IsAlive()) {
1106 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::TreeChange
,
1108 InvalidateFrameSubtree();
1109 PropagateIsUnderHiddenEmbedderElementToSubView(
1110 PresShell()->IsUnderHiddenEmbedderElement() ||
1111 !StyleVisibility()->IsVisible());
1113 if (weakOther
.IsAlive()) {
1114 other
->PresShell()->FrameNeedsReflow(other
, IntrinsicDirty::TreeChange
,
1116 other
->InvalidateFrameSubtree();
1117 other
->PropagateIsUnderHiddenEmbedderElementToSubView(
1118 other
->PresShell()->IsUnderHiddenEmbedderElement() ||
1119 !other
->StyleVisibility()->IsVisible());
1123 void nsSubDocumentFrame::ClearDisplayItems() {
1124 for (nsDisplayItemBase
* i
: DisplayItems()) {
1125 if (i
->GetType() == DisplayItemType::TYPE_SUBDOCUMENT
) {
1126 nsIFrame
* displayRoot
= nsLayoutUtils::GetDisplayRootFrame(this);
1127 MOZ_ASSERT(displayRoot
);
1129 RetainedDisplayListBuilder
* retainedBuilder
=
1130 displayRoot
->GetProperty(RetainedDisplayListBuilder::Cached());
1131 MOZ_ASSERT(retainedBuilder
);
1133 auto* item
= static_cast<nsDisplaySubDocument
*>(i
);
1134 item
->GetChildren()->DeleteAll(retainedBuilder
->Builder());
1141 nsView
* nsSubDocumentFrame::EnsureInnerView() {
1146 // create, init, set the parent of the view
1147 nsView
* outerView
= GetView();
1148 NS_ASSERTION(outerView
, "Must have an outer view already");
1149 nsRect
viewBounds(0, 0, 0, 0); // size will be fixed during reflow
1151 nsViewManager
* viewMan
= outerView
->GetViewManager();
1152 nsView
* innerView
= viewMan
->CreateView(viewBounds
, outerView
);
1154 NS_ERROR("Could not create inner view");
1157 mInnerView
= innerView
;
1158 viewMan
->InsertChild(outerView
, innerView
, nullptr, true);
1163 nsPoint
nsSubDocumentFrame::GetExtraOffset() const {
1164 MOZ_ASSERT(mInnerView
);
1165 return mInnerView
->GetPosition();
1168 void nsSubDocumentFrame::SubdocumentIntrinsicSizeOrRatioChanged() {
1169 if (MOZ_UNLIKELY(HasAllStateBits(NS_FRAME_IS_DIRTY
))) {
1170 // We will be reflowed soon anyway.
1174 const nsStylePosition
* pos
= StylePosition();
1175 bool dependsOnIntrinsics
=
1176 !pos
->mWidth
.ConvertsToLength() || !pos
->mHeight
.ConvertsToLength();
1178 if (dependsOnIntrinsics
|| pos
->mObjectFit
!= StyleObjectFit::Fill
) {
1179 auto dirtyHint
= dependsOnIntrinsics
? IntrinsicDirty::StyleChange
1180 : IntrinsicDirty::Resize
;
1181 PresShell()->FrameNeedsReflow(this, dirtyHint
, NS_FRAME_IS_DIRTY
);
1186 * Gets the layer-pixel offset of aContainerFrame's content rect top-left
1187 * from the nearest display item reference frame (which we assume will be
1188 * inducing a ContainerLayer).
1190 static nsPoint
GetContentRectLayerOffset(nsIFrame
* aContainerFrame
,
1191 nsDisplayListBuilder
* aBuilder
) {
1192 // Offset to the content rect in case we have borders or padding
1193 // Note that aContainerFrame could be a reference frame itself, so
1194 // we need to be careful here to ensure that we call ToReferenceFrame
1195 // on aContainerFrame and not its parent.
1196 nsPoint frameOffset
=
1197 aBuilder
->ToReferenceFrame(aContainerFrame
) +
1198 aContainerFrame
->GetContentRectRelativeToSelf().TopLeft();
1203 // Return true iff |aManager| is a "temporary layer manager". They're
1204 // used for small software rendering tasks, like drawWindow. That's
1205 // currently implemented by a BasicLayerManager without a backing
1206 // widget, and hence in non-retained mode.
1207 inline static bool IsTempLayerManager(LayerManager
* aManager
) {
1208 return (LayersBackend::LAYERS_BASIC
== aManager
->GetBackendType() &&
1209 !static_cast<BasicLayerManager
*>(aManager
)->IsRetained());
1212 nsDisplayRemote::nsDisplayRemote(nsDisplayListBuilder
* aBuilder
,
1213 nsSubDocumentFrame
* aFrame
)
1214 : nsPaintedDisplayItem(aBuilder
, aFrame
),
1216 mEventRegionsOverride(EventRegionsOverride::NoOverride
) {
1217 bool frameIsPointerEventsNone
= aFrame
->StyleUI()->GetEffectivePointerEvents(
1218 aFrame
) == StylePointerEvents::None
;
1219 if (aBuilder
->IsInsidePointerEventsNoneDoc() || frameIsPointerEventsNone
) {
1220 mEventRegionsOverride
|= EventRegionsOverride::ForceEmptyHitRegion
;
1222 if (nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(
1223 aFrame
->PresShell())) {
1224 mEventRegionsOverride
|= EventRegionsOverride::ForceDispatchToContent
;
1227 nsFrameLoader
* frameLoader
= GetFrameLoader();
1228 MOZ_ASSERT(frameLoader
&& frameLoader
->IsRemoteFrame());
1229 if (frameLoader
->GetRemoteBrowser()) {
1230 mLayersId
= frameLoader
->GetLayersId();
1231 mTabId
= frameLoader
->GetRemoteBrowser()->GetTabId();
1235 mozilla::LayerState
nsDisplayRemote::GetLayerState(
1236 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
1237 const ContainerLayerParameters
& aParameters
) {
1238 if (IsTempLayerManager(aManager
)) {
1239 return mozilla::LayerState::LAYER_NONE
;
1241 return mozilla::LayerState::LAYER_ACTIVE_FORCE
;
1244 LayerIntSize
GetFrameSize(const nsIFrame
* aFrame
) {
1245 LayoutDeviceSize size
= LayoutDeviceRect::FromAppUnits(
1246 aFrame
->GetContentRectRelativeToSelf().Size(),
1247 aFrame
->PresContext()->AppUnitsPerDevPixel());
1249 float cumulativeResolution
= aFrame
->PresShell()->GetCumulativeResolution();
1250 return LayerIntSize::Round(size
.width
* cumulativeResolution
,
1251 size
.height
* cumulativeResolution
);
1254 already_AddRefed
<mozilla::layers::Layer
> nsDisplayRemote::BuildLayer(
1255 nsDisplayListBuilder
* aBuilder
, LayerManager
* aManager
,
1256 const ContainerLayerParameters
& aContainerParameters
) {
1257 MOZ_ASSERT(mFrame
, "Makes no sense to have a shadow tree without a frame");
1259 if (IsTempLayerManager(aManager
)) {
1260 // This can happen if aManager is a "temporary" manager, or if the
1261 // widget's layer manager changed out from under us. We need to
1262 // FIXME handle the former case somehow, probably with an API to
1263 // draw a manager's subtree. The latter is bad bad bad, but the the
1264 // MOZ_ASSERT() above will flag it. Returning nullptr here will just
1265 // cause the shadow subtree not to be rendered.
1266 NS_WARNING("Remote iframe not rendered");
1270 if (!mLayersId
.IsValid()) {
1274 if (RefPtr
<RemoteBrowser
> remoteBrowser
=
1275 GetFrameLoader()->GetRemoteBrowser()) {
1276 // Adjust mItemVisibleRect, which is relative to the reference frame, to be
1277 // relative to this frame
1279 if (aContainerParameters
.mItemVisibleRect
) {
1280 visibleRect
= *aContainerParameters
.mItemVisibleRect
;
1282 visibleRect
= GetBuildingRect();
1284 visibleRect
-= ToReferenceFrame();
1285 nsRect contentRect
= Frame()->GetContentRectRelativeToSelf();
1286 visibleRect
.IntersectRect(visibleRect
, contentRect
);
1287 visibleRect
-= contentRect
.TopLeft();
1289 // Generate an effects update notifying the browser it is visible
1290 aBuilder
->AddEffectUpdate(remoteBrowser
,
1291 EffectsInfo::VisibleWithinRect(
1292 visibleRect
, aContainerParameters
.mXScale
,
1293 aContainerParameters
.mYScale
));
1294 // FrameLayerBuilder will take care of notifying the browser when it is no
1298 RefPtr
<Layer
> layer
=
1299 aManager
->GetLayerBuilder()->GetLeafLayerFor(aBuilder
, this);
1302 layer
= aManager
->CreateRefLayer();
1304 if (!layer
|| !layer
->AsRefLayer()) {
1305 // Probably a temporary layer manager that doesn't know how to
1309 RefLayer
* refLayer
= layer
->AsRefLayer();
1311 nsPoint layerOffset
= GetContentRectLayerOffset(Frame(), aBuilder
);
1312 nscoord auPerDevPixel
= Frame()->PresContext()->AppUnitsPerDevPixel();
1313 LayoutDeviceIntPoint offset
=
1314 mozilla::LayoutDeviceIntPoint::FromAppUnitsToNearest(layerOffset
,
1317 // We can only have an offset if we're a child of an inactive
1318 // container, but our display item is LAYER_ACTIVE_FORCE which
1319 // forces all layers above to be active.
1320 MOZ_ASSERT(aContainerParameters
.mOffset
== nsIntPoint());
1321 Matrix4x4 m
= Matrix4x4::Translation(offset
.x
, offset
.y
, 0.0);
1322 // Remote content can't be repainted by us, so we multiply down
1323 // the resolution that our container expects onto our container.
1324 m
.PostScale(aContainerParameters
.mXScale
, aContainerParameters
.mYScale
, 1.0);
1325 refLayer
->SetBaseTransform(m
);
1326 refLayer
->SetEventRegionsOverride(mEventRegionsOverride
);
1327 refLayer
->SetReferentId(mLayersId
);
1328 refLayer
->SetRemoteDocumentSize(GetFrameSize(mFrame
));
1330 return layer
.forget();
1333 void nsDisplayRemote::Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) {
1334 nsPresContext
* pc
= mFrame
->PresContext();
1335 nsFrameLoader
* fl
= GetFrameLoader();
1336 if (pc
->GetPrintSettings() && fl
->IsRemoteFrame()) {
1337 // See the comment below in CreateWebRenderCommands() as for why doing this.
1338 fl
->UpdatePositionAndSize(static_cast<nsSubDocumentFrame
*>(mFrame
));
1341 DrawTarget
* target
= aCtx
->GetDrawTarget();
1342 if (!target
->IsRecording() || mTabId
== 0) {
1343 NS_WARNING("Remote iframe not rendered");
1347 int32_t appUnitsPerDevPixel
= pc
->AppUnitsPerDevPixel();
1349 NSRectToSnappedRect(GetContentRect(), appUnitsPerDevPixel
, *target
);
1350 target
->DrawDependentSurface(mTabId
, destRect
);
1353 bool nsDisplayRemote::CreateWebRenderCommands(
1354 mozilla::wr::DisplayListBuilder
& aBuilder
,
1355 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
1356 const StackingContextHelper
& aSc
,
1357 mozilla::layers::RenderRootStateManager
* aManager
,
1358 nsDisplayListBuilder
* aDisplayListBuilder
) {
1359 if (!mLayersId
.IsValid()) {
1363 nsPresContext
* pc
= mFrame
->PresContext();
1364 nsFrameLoader
* fl
= GetFrameLoader();
1365 if (RefPtr
<RemoteBrowser
> remoteBrowser
= fl
->GetRemoteBrowser()) {
1366 if (pc
->GetPrintSettings()) {
1367 // HACK(emilio): Usually we update sizing/positioning from
1368 // ReflowFinished(). Print documents have no incremental reflow at all
1369 // though, so we can't rely on it firing after a frame becomes remote.
1370 // Thus, if we're painting a remote frame, update its sizing and position
1373 // UpdatePositionAndSize() can cause havoc for non-remote frames but
1374 // luckily we don't care about those, so this is fine.
1375 fl
->UpdatePositionAndSize(static_cast<nsSubDocumentFrame
*>(mFrame
));
1378 // Adjust mItemVisibleRect, which is relative to the reference frame, to be
1379 // relative to this frame
1380 nsRect visibleRect
= GetBuildingRect() - ToReferenceFrame();
1381 nsRect contentRect
= Frame()->GetContentRectRelativeToSelf();
1382 visibleRect
.IntersectRect(visibleRect
, contentRect
);
1383 visibleRect
-= contentRect
.TopLeft();
1385 // Generate an effects update notifying the browser it is visible
1386 // TODO - Gather scaling factors
1387 aDisplayListBuilder
->AddEffectUpdate(
1388 remoteBrowser
, EffectsInfo::VisibleWithinRect(visibleRect
, 1.0f
, 1.0f
));
1390 // Create a WebRenderRemoteData to notify the RemoteBrowser when it is no
1392 RefPtr
<WebRenderRemoteData
> userData
=
1393 aManager
->CommandBuilder()
1394 .CreateOrRecycleWebRenderUserData
<WebRenderRemoteData
>(this,
1396 userData
->SetRemoteBrowser(remoteBrowser
);
1399 nscoord auPerDevPixel
= pc
->AppUnitsPerDevPixel();
1400 nsPoint layerOffset
= GetContentRectLayerOffset(mFrame
, aDisplayListBuilder
);
1401 mOffset
= LayoutDevicePoint::FromAppUnits(layerOffset
, auPerDevPixel
);
1403 nsRect contentRect
= mFrame
->GetContentRectRelativeToSelf();
1404 contentRect
.MoveTo(0, 0);
1405 LayoutDeviceRect rect
=
1406 LayoutDeviceRect::FromAppUnits(contentRect
, auPerDevPixel
);
1409 aBuilder
.PushIFrame(mozilla::wr::ToLayoutRect(rect
), !BackfaceIsHidden(),
1410 mozilla::wr::AsPipelineId(mLayersId
),
1411 /*ignoreMissingPipelines*/ true);
1416 bool nsDisplayRemote::UpdateScrollData(
1417 mozilla::layers::WebRenderScrollData
* aData
,
1418 mozilla::layers::WebRenderLayerScrollData
* aLayerData
) {
1419 if (!mLayersId
.IsValid()) {
1424 aLayerData
->SetReferentId(mLayersId
);
1425 Matrix4x4 m
= Matrix4x4::Translation(mOffset
.x
, mOffset
.y
, 0.0);
1427 // Apply the top level resolution if we are in the same process of the top
1428 // level document. We don't need to apply it in cases where we are in OOP
1429 // iframes since it will be applied later in
1430 // HitTestingTreeNode::GetTransformToGecko by walking up the tree node.
1431 nsPresContext
* inProcessRootContext
=
1432 mFrame
->PresContext()->GetInProcessRootContentDocumentPresContext();
1433 if (inProcessRootContext
&&
1434 inProcessRootContext
->IsRootContentDocumentCrossProcess()) {
1435 float resolution
= inProcessRootContext
->PresShell()->GetResolution();
1436 m
.PostScale(resolution
, resolution
, 1.0);
1439 aLayerData
->SetTransform(m
);
1440 aLayerData
->SetEventRegionsOverride(mEventRegionsOverride
);
1441 aLayerData
->SetRemoteDocumentSize(GetFrameSize(mFrame
));
1446 nsFrameLoader
* nsDisplayRemote::GetFrameLoader() const {
1447 return mFrame
? static_cast<nsSubDocumentFrame
*>(mFrame
)->FrameLoader()