Bug 1758813 [wpt PR 33142] - Implement RP sign out, a=testonly
[gecko.git] / layout / generic / nsSubDocumentFrame.cpp
blob01f05a4b6207cf1d4b499009eee24e7ffd111160
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/. */
7 /*
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"
22 #include "nsCOMPtr.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"
30 #include "nsView.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 "nsContentUtils.h"
43 #include "nsServiceManagerUtils.h"
44 #include "nsQueryObject.h"
45 #include "RetainedDisplayListBuilder.h"
46 #include "nsObjectLoadingContent.h"
48 #include "Layers.h"
49 #include "mozilla/layers/WebRenderUserData.h"
50 #include "mozilla/layers/WebRenderScrollData.h"
51 #include "mozilla/layers/RenderRootStateManager.h"
52 #include "mozilla/layers/StackingContextHelper.h" // for StackingContextHelper
53 #include "mozilla/ProfilerLabels.h"
55 using namespace mozilla;
56 using namespace mozilla::dom;
57 using namespace mozilla::gfx;
58 using namespace mozilla::layers;
60 static Document* GetDocumentFromView(nsView* aView) {
61 MOZ_ASSERT(aView, "null view");
63 nsViewManager* vm = aView->GetViewManager();
64 PresShell* presShell = vm ? vm->GetPresShell() : nullptr;
65 return presShell ? presShell->GetDocument() : nullptr;
68 nsSubDocumentFrame::nsSubDocumentFrame(ComputedStyle* aStyle,
69 nsPresContext* aPresContext)
70 : nsAtomicContainerFrame(aStyle, aPresContext, kClassID),
71 mOuterView(nullptr),
72 mInnerView(nullptr),
73 mIsInline(false),
74 mPostedReflowCallback(false),
75 mDidCreateDoc(false),
76 mCallingShow(false) {}
78 #ifdef ACCESSIBILITY
79 a11y::AccType nsSubDocumentFrame::AccessibleType() {
80 return a11y::eOuterDocType;
82 #endif
84 NS_QUERYFRAME_HEAD(nsSubDocumentFrame)
85 NS_QUERYFRAME_ENTRY(nsSubDocumentFrame)
86 NS_QUERYFRAME_TAIL_INHERITING(nsAtomicContainerFrame)
88 class AsyncFrameInit : public Runnable {
89 public:
90 explicit AsyncFrameInit(nsIFrame* aFrame)
91 : mozilla::Runnable("AsyncFrameInit"), mFrame(aFrame) {}
92 NS_IMETHOD Run() override {
93 AUTO_PROFILER_LABEL("AsyncFrameInit::Run", OTHER);
94 if (mFrame.IsAlive()) {
95 static_cast<nsSubDocumentFrame*>(mFrame.GetFrame())->ShowViewer();
97 return NS_OK;
100 private:
101 WeakFrame mFrame;
104 static void InsertViewsInReverseOrder(nsView* aSibling, nsView* aParent);
106 static void EndSwapDocShellsForViews(nsView* aView);
108 void nsSubDocumentFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
109 nsIFrame* aPrevInFlow) {
110 MOZ_ASSERT(aContent);
111 // determine if we are a <frame> or <iframe>
112 mIsInline = !aContent->IsHTMLElement(nsGkAtoms::frame);
114 nsAtomicContainerFrame::Init(aContent, aParent, aPrevInFlow);
116 // CreateView() creates this frame's view, stored in mOuterView. It needs to
117 // be created first since it's the parent of the inner view, stored in
118 // mInnerView.
119 CreateView();
120 EnsureInnerView();
122 // Set the primary frame now so that nsDocumentViewer::FindContainerView
123 // called from within EndSwapDocShellsForViews below can find it if needed.
124 aContent->SetPrimaryFrame(this);
126 // If we have a detached subdoc's root view on our frame loader, re-insert
127 // it into the view tree. This happens when we've been reframed, and
128 // ensures the presentation persists across reframes. If the frame element
129 // has changed documents however, we blow away the presentation.
130 RefPtr<nsFrameLoader> frameloader = FrameLoader();
131 if (frameloader) {
132 nsCOMPtr<Document> oldContainerDoc;
133 nsIFrame* detachedFrame =
134 frameloader->GetDetachedSubdocFrame(getter_AddRefs(oldContainerDoc));
135 frameloader->SetDetachedSubdocFrame(nullptr, nullptr);
136 MOZ_ASSERT(oldContainerDoc || !detachedFrame);
137 if (oldContainerDoc) {
138 nsView* detachedView = detachedFrame ? detachedFrame->GetView() : nullptr;
139 if (detachedView && oldContainerDoc == aContent->OwnerDoc()) {
140 // Restore stashed presentation.
141 ::InsertViewsInReverseOrder(detachedView, mInnerView);
142 ::EndSwapDocShellsForViews(mInnerView->GetFirstChild());
143 } else {
144 // Presentation is for a different document, don't restore it.
145 frameloader->Hide();
150 PropagateIsUnderHiddenEmbedderElementToSubView(
151 PresShell()->IsUnderHiddenEmbedderElement() ||
152 !StyleVisibility()->IsVisible());
154 nsContentUtils::AddScriptRunner(new AsyncFrameInit(this));
157 void nsSubDocumentFrame::PropagateIsUnderHiddenEmbedderElementToSubView(
158 bool aIsUnderHiddenEmbedderElement) {
159 if (mFrameLoader && mFrameLoader->IsRemoteFrame()) {
160 mFrameLoader->SendIsUnderHiddenEmbedderElement(
161 aIsUnderHiddenEmbedderElement);
162 return;
165 if (!mInnerView) {
166 return;
169 nsView* subdocView = mInnerView->GetFirstChild();
170 while (subdocView) {
171 if (mozilla::PresShell* presShell = subdocView->GetPresShell()) {
172 presShell->SetIsUnderHiddenEmbedderElement(aIsUnderHiddenEmbedderElement);
174 subdocView = subdocView->GetNextSibling();
178 void nsSubDocumentFrame::ShowViewer() {
179 if (mCallingShow) {
180 return;
183 RefPtr<nsFrameLoader> frameloader = FrameLoader();
184 if (!frameloader) {
185 return;
188 if (!frameloader->IsRemoteFrame() && !PresContext()->IsDynamic()) {
189 // We let the printing code take care of loading the document and
190 // initializing the shell; just create the inner view for it to use.
191 (void)EnsureInnerView();
192 } else {
193 AutoWeakFrame weakThis(this);
194 mCallingShow = true;
195 bool didCreateDoc = frameloader->Show(this);
196 if (!weakThis.IsAlive()) {
197 return;
199 mCallingShow = false;
200 mDidCreateDoc = didCreateDoc;
202 if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
203 frameloader->UpdatePositionAndSize(this);
206 if (!weakThis.IsAlive()) {
207 return;
209 InvalidateFrame();
213 nsIFrame* nsSubDocumentFrame::GetSubdocumentRootFrame() {
214 if (!mInnerView) return nullptr;
215 nsView* subdocView = mInnerView->GetFirstChild();
216 return subdocView ? subdocView->GetFrame() : nullptr;
219 mozilla::PresShell* nsSubDocumentFrame::GetSubdocumentPresShellForPainting(
220 uint32_t aFlags) {
221 if (!mInnerView) return nullptr;
223 nsView* subdocView = mInnerView->GetFirstChild();
224 if (!subdocView) return nullptr;
226 mozilla::PresShell* presShell = nullptr;
228 nsIFrame* subdocRootFrame = subdocView->GetFrame();
229 if (subdocRootFrame) {
230 presShell = subdocRootFrame->PresShell();
233 // If painting is suppressed in the presshell, we try to look for a better
234 // presshell to use.
235 if (!presShell || (presShell->IsPaintingSuppressed() &&
236 !(aFlags & IGNORE_PAINT_SUPPRESSION))) {
237 // During page transition mInnerView will sometimes have two children, the
238 // first being the new page that may not have any frame, and the second
239 // being the old page that will probably have a frame.
240 nsView* nextView = subdocView->GetNextSibling();
241 nsIFrame* frame = nullptr;
242 if (nextView) {
243 frame = nextView->GetFrame();
245 if (frame) {
246 mozilla::PresShell* presShellForNextView = frame->PresShell();
247 if (!presShell || (presShellForNextView &&
248 !presShellForNextView->IsPaintingSuppressed() &&
249 StaticPrefs::layout_show_previous_page())) {
250 subdocView = nextView;
251 subdocRootFrame = frame;
252 presShell = presShellForNextView;
255 if (!presShell) {
256 // If we don't have a frame we use this roundabout way to get the pres
257 // shell.
258 if (!mFrameLoader) return nullptr;
259 nsIDocShell* docShell = mFrameLoader->GetDocShell(IgnoreErrors());
260 if (!docShell) return nullptr;
261 presShell = docShell->GetPresShell();
265 return presShell;
268 ScreenIntSize nsSubDocumentFrame::GetSubdocumentSize() {
269 if (HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
270 if (RefPtr<nsFrameLoader> frameloader = FrameLoader()) {
271 nsCOMPtr<Document> oldContainerDoc;
272 nsIFrame* detachedFrame =
273 frameloader->GetDetachedSubdocFrame(getter_AddRefs(oldContainerDoc));
274 if (nsView* view = detachedFrame ? detachedFrame->GetView() : nullptr) {
275 nsSize size = view->GetBounds().Size();
276 nsPresContext* presContext = detachedFrame->PresContext();
277 return ScreenIntSize(presContext->AppUnitsToDevPixels(size.width),
278 presContext->AppUnitsToDevPixels(size.height));
281 // Pick some default size for now. Using 10x10 because that's what the
282 // code used to do.
283 return ScreenIntSize(10, 10);
286 nsSize docSizeAppUnits;
287 nsPresContext* presContext = PresContext();
288 if (GetContent()->IsHTMLElement(nsGkAtoms::frame)) {
289 docSizeAppUnits = GetSize();
290 } else {
291 docSizeAppUnits = GetContentRect().Size();
294 // Adjust subdocument size, according to 'object-fit' and the subdocument's
295 // intrinsic size and ratio.
296 docSizeAppUnits = nsLayoutUtils::ComputeObjectDestRect(
297 nsRect(nsPoint(), docSizeAppUnits), GetIntrinsicSize(),
298 GetIntrinsicRatio(), StylePosition())
299 .Size();
301 return ScreenIntSize(
302 presContext->AppUnitsToDevPixels(docSizeAppUnits.width),
303 presContext->AppUnitsToDevPixels(docSizeAppUnits.height));
306 static void WrapBackgroundColorInOwnLayer(nsDisplayListBuilder* aBuilder,
307 nsIFrame* aFrame,
308 nsDisplayList* aList) {
309 for (nsDisplayItem* item : aList->TakeItems()) {
310 if (item->GetType() == DisplayItemType::TYPE_BACKGROUND_COLOR) {
311 nsDisplayList tmpList(aBuilder);
312 tmpList.AppendToTop(item);
313 item = MakeDisplayItemWithIndex<nsDisplayOwnLayer>(
314 aBuilder, aFrame, /* aIndex = */ nsDisplayOwnLayer::OwnLayerForSubdoc,
315 &tmpList, aBuilder->CurrentActiveScrolledRoot(),
316 nsDisplayOwnLayerFlags::None, ScrollbarData{}, true, false);
318 aList->AppendToTop(item);
322 void nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
323 const nsDisplayListSet& aLists) {
324 if (!IsVisibleForPainting()) {
325 return;
328 nsFrameLoader* frameLoader = FrameLoader();
329 bool isRemoteFrame = frameLoader && frameLoader->IsRemoteFrame();
331 // If we are pointer-events:none then we don't need to HitTest background
332 const bool pointerEventsNone =
333 Style()->PointerEvents() == StylePointerEvents::None;
334 if (!aBuilder->IsForEventDelivery() || !pointerEventsNone) {
335 nsDisplayListCollection decorations(aBuilder);
336 DisplayBorderBackgroundOutline(aBuilder, decorations);
337 if (isRemoteFrame) {
338 // Wrap background colors of <iframe>s with remote subdocuments in their
339 // own layer so we generate a ColorLayer. This is helpful for optimizing
340 // compositing; we can skip compositing the ColorLayer when the
341 // remote content is opaque.
342 WrapBackgroundColorInOwnLayer(aBuilder, this,
343 decorations.BorderBackground());
345 decorations.MoveTo(aLists);
348 if (aBuilder->IsForEventDelivery() && pointerEventsNone) {
349 return;
352 // If we're passing pointer events to children then we have to descend into
353 // subdocuments no matter what, to determine which parts are transparent for
354 // hit-testing or event regions.
355 bool needToDescend = aBuilder->GetDescendIntoSubdocuments();
356 if (!mInnerView || !needToDescend) {
357 return;
360 if (isRemoteFrame) {
361 // We're the subdoc for <browser remote="true"> and it has
362 // painted content. Display its shadow layer tree.
363 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
364 clipState.ClipContainingBlockDescendantsToContentBox(aBuilder, this);
366 aLists.Content()->AppendNewToTop<nsDisplayRemote>(aBuilder, this);
367 return;
370 RefPtr<mozilla::PresShell> presShell = GetSubdocumentPresShellForPainting(
371 aBuilder->IsIgnoringPaintSuppression() ? IGNORE_PAINT_SUPPRESSION : 0);
373 if (!presShell) {
374 return;
377 if (aBuilder->IsInFilter()) {
378 Document* outerDoc = PresShell()->GetDocument();
379 Document* innerDoc = presShell->GetDocument();
380 if (outerDoc && innerDoc) {
381 if (!outerDoc->NodePrincipal()->Equals(innerDoc->NodePrincipal())) {
382 outerDoc->SetUseCounter(eUseCounter_custom_FilteredCrossOriginIFrame);
387 nsIFrame* subdocRootFrame = presShell->GetRootFrame();
389 nsPresContext* presContext = presShell->GetPresContext();
391 int32_t parentAPD = PresContext()->AppUnitsPerDevPixel();
392 int32_t subdocAPD = presContext->AppUnitsPerDevPixel();
394 nsRect visible;
395 nsRect dirty;
396 bool ignoreViewportScrolling = false;
397 if (subdocRootFrame) {
398 // get the dirty rect relative to the root frame of the subdoc
399 visible = aBuilder->GetVisibleRect() + GetOffsetToCrossDoc(subdocRootFrame);
400 dirty = aBuilder->GetDirtyRect() + GetOffsetToCrossDoc(subdocRootFrame);
401 // and convert into the appunits of the subdoc
402 visible = visible.ScaleToOtherAppUnitsRoundOut(parentAPD, subdocAPD);
403 dirty = dirty.ScaleToOtherAppUnitsRoundOut(parentAPD, subdocAPD);
405 if (nsIScrollableFrame* rootScrollableFrame =
406 presShell->GetRootScrollFrameAsScrollable()) {
407 // Use a copy, so the rects don't get modified.
408 nsRect copyOfDirty = dirty;
409 nsRect copyOfVisible = visible;
410 // TODO(botond): Can we just axe this DecideScrollableLayer call?
411 rootScrollableFrame->DecideScrollableLayer(aBuilder, &copyOfVisible,
412 &copyOfDirty,
413 /* aSetBase = */ true);
415 ignoreViewportScrolling = presShell->IgnoringViewportScrolling();
418 aBuilder->EnterPresShell(subdocRootFrame, pointerEventsNone);
419 aBuilder->IncrementPresShellPaintCount(presShell);
420 } else {
421 visible = aBuilder->GetVisibleRect();
422 dirty = aBuilder->GetDirtyRect();
425 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
426 clipState.ClipContainingBlockDescendantsToContentBox(aBuilder, this);
428 nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
429 bool constructZoomItem = subdocRootFrame && parentAPD != subdocAPD;
430 bool needsOwnLayer = false;
431 if (constructZoomItem || presContext->IsRootContentDocumentCrossProcess() ||
432 (sf && sf->IsScrollingActive())) {
433 needsOwnLayer = true;
436 nsDisplayList childItems(aBuilder);
439 DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
440 if (needsOwnLayer) {
441 // Clear current clip. There's no point in propagating it down, since
442 // the layer we will construct will be clipped by the current clip.
443 // In fact for nsDisplayZoom propagating it down would be incorrect since
444 // nsDisplayZoom changes the meaning of appunits.
445 nestedClipState.Clear();
448 // Invoke AutoBuildingDisplayList to ensure that the correct dirty rect
449 // is used to compute the visible rect if AddCanvasBackgroundColorItem
450 // creates a display item.
451 nsIFrame* frame = subdocRootFrame ? subdocRootFrame : this;
452 nsDisplayListBuilder::AutoBuildingDisplayList building(aBuilder, frame,
453 visible, dirty);
455 if (subdocRootFrame) {
456 nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
457 nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(
458 aBuilder,
459 ignoreViewportScrolling && rootScrollFrame &&
460 rootScrollFrame->GetContent()
461 ? nsLayoutUtils::FindOrCreateIDFor(rootScrollFrame->GetContent())
462 : aBuilder->GetCurrentScrollParentId());
464 bool hasDocumentLevelListenersForApzAwareEvents =
465 gfxPlatform::AsyncPanZoomEnabled() &&
466 nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(presShell);
468 aBuilder->SetAncestorHasApzAwareEventHandler(
469 hasDocumentLevelListenersForApzAwareEvents);
470 subdocRootFrame->BuildDisplayListForStackingContext(aBuilder,
471 &childItems);
474 if (!aBuilder->IsForEventDelivery()) {
475 // If we are going to use a displayzoom below then any items we put under
476 // it need to have underlying frames from the subdocument. So we need to
477 // calculate the bounds based on which frame will be the underlying frame
478 // for the canvas background color item.
479 nsRect bounds =
480 GetContentRectRelativeToSelf() + aBuilder->ToReferenceFrame(this);
481 if (subdocRootFrame) {
482 bounds = bounds.ScaleToOtherAppUnitsRoundOut(parentAPD, subdocAPD);
485 // If we are in print preview/page layout we want to paint the grey
486 // background behind the page, not the canvas color. The canvas color gets
487 // painted on the page itself.
488 if (nsLayoutUtils::NeedsPrintPreviewBackground(presContext)) {
489 presShell->AddPrintPreviewBackgroundItem(aBuilder, &childItems, frame,
490 bounds);
491 } else {
492 // Add the canvas background color to the bottom of the list. This
493 // happens after we've built the list so that
494 // AddCanvasBackgroundColorItem can monkey with the contents if
495 // necessary.
496 presShell->AddCanvasBackgroundColorItem(
497 aBuilder, &childItems, frame, bounds, NS_RGBA(0, 0, 0, 0),
498 AddCanvasBackgroundColorFlags::ForceDraw);
503 if (subdocRootFrame) {
504 aBuilder->LeavePresShell(subdocRootFrame, &childItems);
507 // Generate a resolution and/or zoom item if needed. If one or both of those
508 // is created, we don't need to create a separate nsDisplaySubDocument.
510 nsDisplayOwnLayerFlags flags =
511 nsDisplayOwnLayerFlags::GenerateSubdocInvalidations;
512 // If ignoreViewportScrolling is true then the top most layer we create here
513 // is going to become the scrollable layer for the root scroll frame, so we
514 // want to add nsDisplayOwnLayer::GENERATE_SCROLLABLE_LAYER to whatever layer
515 // becomes the topmost. We do this below.
516 if (constructZoomItem) {
517 nsDisplayOwnLayerFlags zoomFlags = flags;
518 if (ignoreViewportScrolling) {
519 zoomFlags |= nsDisplayOwnLayerFlags::GenerateScrollableLayer;
521 childItems.AppendNewToTop<nsDisplayZoom>(aBuilder, subdocRootFrame, this,
522 &childItems, subdocAPD, parentAPD,
523 zoomFlags);
525 needsOwnLayer = false;
527 // Wrap the zoom item in the resolution item if we have both because we want
528 // the resolution scale applied on top of the app units per dev pixel
529 // conversion.
530 if (ignoreViewportScrolling) {
531 flags |= nsDisplayOwnLayerFlags::GenerateScrollableLayer;
534 // We always want top level content documents to be in their own layer.
535 nsDisplaySubDocument* layerItem = MakeDisplayItem<nsDisplaySubDocument>(
536 aBuilder, subdocRootFrame ? subdocRootFrame : this, this, &childItems,
537 flags);
538 if (layerItem) {
539 childItems.AppendToTop(layerItem);
540 layerItem->SetShouldFlattenAway(!needsOwnLayer);
543 if (aBuilder->IsForFrameVisibility()) {
544 // We don't add the childItems to the return list as we're dealing with them
545 // here.
546 presShell->RebuildApproximateFrameVisibilityDisplayList(childItems);
547 childItems.DeleteAll(aBuilder);
548 } else {
549 aLists.Content()->AppendToTop(&childItems);
553 #ifdef DEBUG_FRAME_DUMP
554 void nsSubDocumentFrame::List(FILE* out, const char* aPrefix,
555 ListFlags aFlags) const {
556 nsCString str;
557 ListGeneric(str, aPrefix, aFlags);
558 fprintf_stderr(out, "%s\n", str.get());
560 if (aFlags.contains(ListFlag::TraverseSubdocumentFrames)) {
561 nsSubDocumentFrame* f = const_cast<nsSubDocumentFrame*>(this);
562 nsIFrame* subdocRootFrame = f->GetSubdocumentRootFrame();
563 if (subdocRootFrame) {
564 nsCString pfx(aPrefix);
565 pfx += " ";
566 subdocRootFrame->List(out, pfx.get(), aFlags);
571 nsresult nsSubDocumentFrame::GetFrameName(nsAString& aResult) const {
572 return MakeFrameName(u"FrameOuter"_ns, aResult);
574 #endif
576 /* virtual */
577 nscoord nsSubDocumentFrame::GetMinISize(gfxContext* aRenderingContext) {
578 nscoord result;
579 DISPLAY_MIN_INLINE_SIZE(this, result);
581 nsCOMPtr<nsIObjectLoadingContent> iolc = do_QueryInterface(mContent);
582 auto olc = static_cast<nsObjectLoadingContent*>(iolc.get());
584 if (olc && olc->GetSubdocumentIntrinsicSize()) {
585 // The subdocument is an SVG document, so technically we should call
586 // SVGOuterSVGFrame::GetMinISize() on its root frame. That method always
587 // returns 0, though, so we can just do that & don't need to bother with
588 // the cross-doc communication.
589 result = 0;
590 } else {
591 result = GetIntrinsicISize();
594 return result;
597 /* virtual */
598 nscoord nsSubDocumentFrame::GetPrefISize(gfxContext* aRenderingContext) {
599 nscoord result;
600 DISPLAY_PREF_INLINE_SIZE(this, result);
602 // If the subdocument is an SVG document, then in theory we want to return
603 // the same thing that SVGOuterSVGFrame::GetPrefISize does. That method
604 // has some special handling of percentage values to avoid unhelpful zero
605 // sizing in the presence of orthogonal writing modes. We don't bother
606 // with that for SVG documents in <embed> and <object>, since that special
607 // handling doesn't look up across document boundaries anyway.
608 result = GetIntrinsicISize();
610 return result;
613 /* virtual */
614 IntrinsicSize nsSubDocumentFrame::GetIntrinsicSize() {
615 if (StyleDisplay()->IsContainSize()) {
616 // Intrinsic size of 'contain:size' replaced elements is 0,0.
617 return IntrinsicSize(0, 0);
620 if (nsCOMPtr<nsIObjectLoadingContent> iolc = do_QueryInterface(mContent)) {
621 auto olc = static_cast<nsObjectLoadingContent*>(iolc.get());
623 if (auto size = olc->GetSubdocumentIntrinsicSize()) {
624 // Use the intrinsic size from the child SVG document, if available.
625 return *size;
629 if (!IsInline()) {
630 return {}; // <frame> elements have no useful intrinsic size.
633 if (mContent->IsXULElement()) {
634 return {}; // XUL <iframe> and <browser> have no useful intrinsic size
637 // We must be an HTML <iframe>. Return fallback size.
638 return IntrinsicSize(kFallbackIntrinsicSize);
641 /* virtual */
642 AspectRatio nsSubDocumentFrame::GetIntrinsicRatio() const {
643 // FIXME(emilio): This should probably respect contain: size and return no
644 // ratio in the case subDocRoot is non-null. Otherwise we do it by virtue of
645 // using a zero-size below and reusing GetIntrinsicSize().
646 if (nsCOMPtr<nsIObjectLoadingContent> iolc = do_QueryInterface(mContent)) {
647 auto olc = static_cast<nsObjectLoadingContent*>(iolc.get());
649 auto ratio = olc->GetSubdocumentIntrinsicRatio();
650 if (ratio && *ratio) {
651 // Use the intrinsic aspect ratio from the child SVG document, if
652 // available.
653 return *ratio;
657 // NOTE(emilio): Even though we have an intrinsic size, we may not have an
658 // intrinsic ratio. For example `<iframe style="width: 100px">` should not
659 // shrink in the vertical axis to preserve the 300x150 ratio.
660 return nsAtomicContainerFrame::GetIntrinsicRatio();
663 /* virtual */
664 LogicalSize nsSubDocumentFrame::ComputeAutoSize(
665 gfxContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize,
666 nscoord aAvailableISize, const LogicalSize& aMargin,
667 const LogicalSize& aBorderPadding, const StyleSizeOverrides& aSizeOverrides,
668 ComputeSizeFlags aFlags) {
669 if (!IsInline()) {
670 return nsIFrame::ComputeAutoSize(aRenderingContext, aWM, aCBSize,
671 aAvailableISize, aMargin, aBorderPadding,
672 aSizeOverrides, aFlags);
675 const WritingMode wm = GetWritingMode();
676 LogicalSize result(wm, GetIntrinsicISize(), GetIntrinsicBSize());
677 return result.ConvertTo(aWM, wm);
680 /* virtual */
681 nsIFrame::SizeComputationResult nsSubDocumentFrame::ComputeSize(
682 gfxContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize,
683 nscoord aAvailableISize, const LogicalSize& aMargin,
684 const LogicalSize& aBorderPadding, const StyleSizeOverrides& aSizeOverrides,
685 ComputeSizeFlags aFlags) {
686 return {ComputeSizeWithIntrinsicDimensions(
687 aRenderingContext, aWM, GetIntrinsicSize(), GetAspectRatio(),
688 aCBSize, aMargin, aBorderPadding, aSizeOverrides, aFlags),
689 AspectRatioUsage::None};
692 void nsSubDocumentFrame::Reflow(nsPresContext* aPresContext,
693 ReflowOutput& aDesiredSize,
694 const ReflowInput& aReflowInput,
695 nsReflowStatus& aStatus) {
696 MarkInReflow();
697 DO_GLOBAL_REFLOW_COUNT("nsSubDocumentFrame");
698 DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
699 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
700 NS_FRAME_TRACE(
701 NS_FRAME_TRACE_CALLS,
702 ("enter nsSubDocumentFrame::Reflow: maxSize=%d,%d",
703 aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()));
705 NS_ASSERTION(aReflowInput.ComputedWidth() != NS_UNCONSTRAINEDSIZE,
706 "Shouldn't have unconstrained stuff here "
707 "thanks to the rules of reflow");
708 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aReflowInput.ComputedHeight(),
709 "Shouldn't have unconstrained stuff here "
710 "thanks to ComputeAutoSize");
712 NS_ASSERTION(mContent->GetPrimaryFrame() == this, "Shouldn't happen");
714 // XUL <iframe> or <browser>, or HTML <iframe>, <object> or <embed>
715 const auto wm = aReflowInput.GetWritingMode();
716 aDesiredSize.SetSize(wm, aReflowInput.ComputedSizeWithBorderPadding(wm));
718 // "offset" is the offset of our content area from our frame's
719 // top-left corner.
720 nsPoint offset = nsPoint(aReflowInput.ComputedPhysicalBorderPadding().left,
721 aReflowInput.ComputedPhysicalBorderPadding().top);
723 if (mInnerView) {
724 const nsMargin& bp = aReflowInput.ComputedPhysicalBorderPadding();
725 nsSize innerSize(aDesiredSize.Width() - bp.LeftRight(),
726 aDesiredSize.Height() - bp.TopBottom());
728 // Size & position the view according to 'object-fit' & 'object-position'.
729 nsRect destRect = nsLayoutUtils::ComputeObjectDestRect(
730 nsRect(offset, innerSize), GetIntrinsicSize(), GetIntrinsicRatio(),
731 StylePosition());
733 nsViewManager* vm = mInnerView->GetViewManager();
734 vm->MoveViewTo(mInnerView, destRect.x, destRect.y);
735 vm->ResizeView(mInnerView, nsRect(nsPoint(0, 0), destRect.Size()), true);
738 aDesiredSize.SetOverflowAreasToDesiredBounds();
740 FinishAndStoreOverflow(&aDesiredSize);
742 if (!aPresContext->IsRootPaginatedDocument() && !mPostedReflowCallback) {
743 PresShell()->PostReflowCallback(this);
744 mPostedReflowCallback = true;
747 NS_FRAME_TRACE(
748 NS_FRAME_TRACE_CALLS,
749 ("exit nsSubDocumentFrame::Reflow: size=%d,%d status=%s",
750 aDesiredSize.Width(), aDesiredSize.Height(), ToString(aStatus).c_str()));
752 NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
755 bool nsSubDocumentFrame::ReflowFinished() {
756 RefPtr<nsFrameLoader> frameloader = FrameLoader();
757 if (frameloader) {
758 AutoWeakFrame weakFrame(this);
760 frameloader->UpdatePositionAndSize(this);
762 if (weakFrame.IsAlive()) {
763 // Make sure that we can post a reflow callback in the future.
764 mPostedReflowCallback = false;
766 } else {
767 mPostedReflowCallback = false;
769 return false;
772 void nsSubDocumentFrame::ReflowCallbackCanceled() {
773 mPostedReflowCallback = false;
776 nsresult nsSubDocumentFrame::AttributeChanged(int32_t aNameSpaceID,
777 nsAtom* aAttribute,
778 int32_t aModType) {
779 if (aNameSpaceID != kNameSpaceID_None) {
780 return NS_OK;
783 // If the noResize attribute changes, dis/allow frame to be resized
784 if (aAttribute == nsGkAtoms::noresize) {
785 // Note that we're not doing content type checks, but that's ok -- if
786 // they'd fail we will just end up with a null framesetFrame.
787 if (mContent->GetParent()->IsHTMLElement(nsGkAtoms::frameset)) {
788 nsIFrame* parentFrame = GetParent();
790 if (parentFrame) {
791 // There is no interface for nsHTMLFramesetFrame so QI'ing to
792 // concrete class, yay!
793 nsHTMLFramesetFrame* framesetFrame = do_QueryFrame(parentFrame);
794 if (framesetFrame) {
795 framesetFrame->RecalculateBorderResize();
799 } else if (aAttribute == nsGkAtoms::marginwidth ||
800 aAttribute == nsGkAtoms::marginheight) {
801 // Notify the frameloader
802 if (RefPtr<nsFrameLoader> frameloader = FrameLoader()) {
803 frameloader->MarginsChanged();
807 return NS_OK;
810 void nsSubDocumentFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
811 nsAtomicContainerFrame::DidSetComputedStyle(aOldComputedStyle);
813 // If this presshell has invisible ancestors, we don't need to propagate the
814 // visibility style change to the subdocument since the subdocument should
815 // have already set the IsUnderHiddenEmbedderElement flag in
816 // nsSubDocumentFrame::Init.
817 if (PresShell()->IsUnderHiddenEmbedderElement()) {
818 return;
821 const bool isVisible = StyleVisibility()->IsVisible();
822 if (!aOldComputedStyle ||
823 isVisible != aOldComputedStyle->StyleVisibility()->IsVisible()) {
824 PropagateIsUnderHiddenEmbedderElementToSubView(!isVisible);
828 nsIFrame* NS_NewSubDocumentFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
829 return new (aPresShell)
830 nsSubDocumentFrame(aStyle, aPresShell->GetPresContext());
833 NS_IMPL_FRAMEARENA_HELPERS(nsSubDocumentFrame)
835 class nsHideViewer : public Runnable {
836 public:
837 nsHideViewer(nsIContent* aFrameElement, nsFrameLoader* aFrameLoader,
838 PresShell* aPresShell, bool aHideViewerIfFrameless)
839 : mozilla::Runnable("nsHideViewer"),
840 mFrameElement(aFrameElement),
841 mFrameLoader(aFrameLoader),
842 mPresShell(aPresShell),
843 mHideViewerIfFrameless(aHideViewerIfFrameless) {
844 NS_ASSERTION(mFrameElement, "Must have a frame element");
845 NS_ASSERTION(mFrameLoader, "Must have a frame loader");
846 NS_ASSERTION(mPresShell, "Must have a presshell");
849 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override {
850 // Flush frames, to ensure any pending display:none changes are made.
851 // Note it can be unsafe to flush if we've destroyed the presentation
852 // for some other reason, like if we're shutting down.
854 // But avoid the flush if we know for sure we're away, like when we're out
855 // of the document already.
857 // FIXME(emilio): This could still be a perf footgun when removing lots of
858 // siblings where each of them cause the reframe of an ancestor which happen
859 // to contain a subdocument.
861 // We should find some way to avoid that!
862 if (!mPresShell->IsDestroying() && mFrameElement->IsInComposedDoc()) {
863 MOZ_KnownLive(mPresShell)->FlushPendingNotifications(FlushType::Frames);
866 // Either the frame has been constructed by now, or it never will be,
867 // either way we want to clear the stashed views.
868 mFrameLoader->SetDetachedSubdocFrame(nullptr, nullptr);
870 nsSubDocumentFrame* frame = do_QueryFrame(mFrameElement->GetPrimaryFrame());
871 if ((!frame && mHideViewerIfFrameless) || mPresShell->IsDestroying()) {
872 // Either the frame element has no nsIFrame or the presshell is being
873 // destroyed. Hide the nsFrameLoader, which destroys the presentation.
874 mFrameLoader->Hide();
876 return NS_OK;
879 private:
880 nsCOMPtr<nsIContent> mFrameElement;
881 RefPtr<nsFrameLoader> mFrameLoader;
882 RefPtr<PresShell> mPresShell;
883 bool mHideViewerIfFrameless;
886 static nsView* BeginSwapDocShellsForViews(nsView* aSibling);
888 void nsSubDocumentFrame::DestroyFrom(nsIFrame* aDestructRoot,
889 PostDestroyData& aPostDestroyData) {
890 PropagateIsUnderHiddenEmbedderElementToSubView(true);
891 if (mPostedReflowCallback) {
892 PresShell()->CancelReflowCallback(this);
893 mPostedReflowCallback = false;
896 // Detach the subdocument's views and stash them in the frame loader.
897 // We can then reattach them if we're being reframed (for example if
898 // the frame has been made position:fixed).
899 RefPtr<nsFrameLoader> frameloader = FrameLoader();
900 if (frameloader) {
901 nsView* detachedViews =
902 ::BeginSwapDocShellsForViews(mInnerView->GetFirstChild());
904 if (detachedViews && detachedViews->GetFrame()) {
905 frameloader->SetDetachedSubdocFrame(detachedViews->GetFrame(),
906 mContent->OwnerDoc());
908 // We call nsFrameLoader::HideViewer() in a script runner so that we can
909 // safely determine whether the frame is being reframed or destroyed.
910 nsContentUtils::AddScriptRunner(new nsHideViewer(
911 mContent, frameloader, PresShell(), (mDidCreateDoc || mCallingShow)));
912 } else {
913 frameloader->SetDetachedSubdocFrame(nullptr, nullptr);
914 if (mDidCreateDoc || mCallingShow) {
915 frameloader->Hide();
920 nsAtomicContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
923 nsFrameLoader* nsSubDocumentFrame::FrameLoader() const {
924 if (mFrameLoader) {
925 return mFrameLoader;
928 if (RefPtr<nsFrameLoaderOwner> loaderOwner = do_QueryObject(GetContent())) {
929 mFrameLoader = loaderOwner->GetFrameLoader();
932 return mFrameLoader;
935 auto nsSubDocumentFrame::GetRemotePaintData() const -> RemoteFramePaintData {
936 if (mRetainedRemoteFrame) {
937 return *mRetainedRemoteFrame;
940 RemoteFramePaintData data;
941 nsFrameLoader* fl = FrameLoader();
942 if (!fl) {
943 return data;
946 auto* rb = fl->GetRemoteBrowser();
947 if (!rb) {
948 return data;
950 data.mLayersId = rb->GetLayersId();
951 data.mTabId = rb->GetTabId();
952 return data;
955 void nsSubDocumentFrame::ResetFrameLoader(RetainPaintData aRetain) {
956 if (aRetain == RetainPaintData::Yes && mFrameLoader) {
957 mRetainedRemoteFrame = Some(GetRemotePaintData());
958 } else {
959 mRetainedRemoteFrame.reset();
961 mFrameLoader = nullptr;
962 ClearDisplayItems();
963 nsContentUtils::AddScriptRunner(new AsyncFrameInit(this));
966 void nsSubDocumentFrame::ClearRetainedPaintData() {
967 mRetainedRemoteFrame.reset();
968 ClearDisplayItems();
969 InvalidateFrameSubtree();
972 // XXX this should be called ObtainDocShell or something like that,
973 // to indicate that it could have side effects
974 nsIDocShell* nsSubDocumentFrame::GetDocShell() const {
975 // How can FrameLoader() return null???
976 if (NS_WARN_IF(!FrameLoader())) {
977 return nullptr;
979 return mFrameLoader->GetDocShell(IgnoreErrors());
982 static void DestroyDisplayItemDataForFrames(nsIFrame* aFrame) {
983 // Destroying a WebRenderUserDataTable can cause destruction of other objects
984 // which can remove frame properties in their destructor. If we delete a frame
985 // property it runs the destructor of the stored object in the middle of
986 // updating the frame property table, so if the destruction of that object
987 // causes another update to the frame property table it would leave the frame
988 // property table in an inconsistent state. So we remove it from the table and
989 // then destroy it. (bug 1530657)
990 WebRenderUserDataTable* userDataTable =
991 aFrame->TakeProperty(WebRenderUserDataProperty::Key());
992 if (userDataTable) {
993 for (const auto& data : userDataTable->Values()) {
994 data->RemoveFromTable();
996 delete userDataTable;
999 for (const auto& childList : aFrame->ChildLists()) {
1000 for (nsIFrame* child : childList.mList) {
1001 DestroyDisplayItemDataForFrames(child);
1006 static CallState BeginSwapDocShellsForDocument(Document& aDocument) {
1007 if (PresShell* presShell = aDocument.GetPresShell()) {
1008 // Disable painting while the views are detached, see bug 946929.
1009 presShell->SetNeverPainting(true);
1011 if (nsIFrame* rootFrame = presShell->GetRootFrame()) {
1012 ::DestroyDisplayItemDataForFrames(rootFrame);
1015 aDocument.EnumerateSubDocuments(BeginSwapDocShellsForDocument);
1016 return CallState::Continue;
1019 static nsView* BeginSwapDocShellsForViews(nsView* aSibling) {
1020 // Collect the removed sibling views in reverse order in 'removedViews'.
1021 nsView* removedViews = nullptr;
1022 while (aSibling) {
1023 if (Document* doc = ::GetDocumentFromView(aSibling)) {
1024 ::BeginSwapDocShellsForDocument(*doc);
1026 nsView* next = aSibling->GetNextSibling();
1027 aSibling->GetViewManager()->RemoveChild(aSibling);
1028 aSibling->SetNextSibling(removedViews);
1029 removedViews = aSibling;
1030 aSibling = next;
1032 return removedViews;
1035 static void InsertViewsInReverseOrder(nsView* aSibling, nsView* aParent) {
1036 MOZ_ASSERT(aParent, "null view");
1037 MOZ_ASSERT(!aParent->GetFirstChild(), "inserting into non-empty list");
1039 nsViewManager* vm = aParent->GetViewManager();
1040 while (aSibling) {
1041 nsView* next = aSibling->GetNextSibling();
1042 aSibling->SetNextSibling(nullptr);
1043 // true means 'after' in document order which is 'before' in view order,
1044 // so this call prepends the child, thus reversing the siblings as we go.
1045 vm->InsertChild(aParent, aSibling, nullptr, true);
1046 aSibling = next;
1050 nsresult nsSubDocumentFrame::BeginSwapDocShells(nsIFrame* aOther) {
1051 if (!aOther || !aOther->IsSubDocumentFrame()) {
1052 return NS_ERROR_NOT_IMPLEMENTED;
1055 nsSubDocumentFrame* other = static_cast<nsSubDocumentFrame*>(aOther);
1056 if (!mFrameLoader || !mDidCreateDoc || mCallingShow || !other->mFrameLoader ||
1057 !other->mDidCreateDoc) {
1058 return NS_ERROR_NOT_IMPLEMENTED;
1061 ClearDisplayItems();
1062 other->ClearDisplayItems();
1064 if (mInnerView && other->mInnerView) {
1065 nsView* ourSubdocViews = mInnerView->GetFirstChild();
1066 nsView* ourRemovedViews = ::BeginSwapDocShellsForViews(ourSubdocViews);
1067 nsView* otherSubdocViews = other->mInnerView->GetFirstChild();
1068 nsView* otherRemovedViews = ::BeginSwapDocShellsForViews(otherSubdocViews);
1070 ::InsertViewsInReverseOrder(ourRemovedViews, other->mInnerView);
1071 ::InsertViewsInReverseOrder(otherRemovedViews, mInnerView);
1073 mFrameLoader.swap(other->mFrameLoader);
1074 return NS_OK;
1077 static CallState EndSwapDocShellsForDocument(Document& aDocument) {
1078 // Our docshell and view trees have been updated for the new hierarchy.
1079 // Now also update all nsDeviceContext::mWidget to that of the
1080 // container view in the new hierarchy.
1081 if (nsCOMPtr<nsIDocShell> ds = aDocument.GetDocShell()) {
1082 nsCOMPtr<nsIContentViewer> cv;
1083 ds->GetContentViewer(getter_AddRefs(cv));
1084 while (cv) {
1085 RefPtr<nsPresContext> pc = cv->GetPresContext();
1086 if (pc && pc->GetPresShell()) {
1087 pc->GetPresShell()->SetNeverPainting(ds->IsInvisible());
1089 nsDeviceContext* dc = pc ? pc->DeviceContext() : nullptr;
1090 if (dc) {
1091 nsView* v = cv->FindContainerView();
1092 dc->Init(v ? v->GetNearestWidget(nullptr) : nullptr);
1094 cv = cv->GetPreviousViewer();
1098 aDocument.EnumerateSubDocuments(EndSwapDocShellsForDocument);
1099 return CallState::Continue;
1102 static void EndSwapDocShellsForViews(nsView* aSibling) {
1103 for (; aSibling; aSibling = aSibling->GetNextSibling()) {
1104 if (Document* doc = ::GetDocumentFromView(aSibling)) {
1105 ::EndSwapDocShellsForDocument(*doc);
1107 nsIFrame* frame = aSibling->GetFrame();
1108 if (frame) {
1109 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame);
1110 if (parent->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
1111 nsIFrame::AddInPopupStateBitToDescendants(frame);
1112 } else {
1113 nsIFrame::RemoveInPopupStateBitFromDescendants(frame);
1115 if (frame->HasInvalidFrameInSubtree()) {
1116 while (parent &&
1117 !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT |
1118 NS_FRAME_IS_NONDISPLAY)) {
1119 parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
1120 parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(parent);
1127 void nsSubDocumentFrame::EndSwapDocShells(nsIFrame* aOther) {
1128 nsSubDocumentFrame* other = static_cast<nsSubDocumentFrame*>(aOther);
1129 AutoWeakFrame weakThis(this);
1130 AutoWeakFrame weakOther(aOther);
1132 if (mInnerView) {
1133 ::EndSwapDocShellsForViews(mInnerView->GetFirstChild());
1135 if (other->mInnerView) {
1136 ::EndSwapDocShellsForViews(other->mInnerView->GetFirstChild());
1139 // Now make sure we reflow both frames, in case their contents
1140 // determine their size.
1141 // And repaint them, for good measure, in case there's nothing
1142 // interesting that happens during reflow.
1143 if (weakThis.IsAlive()) {
1144 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::TreeChange,
1145 NS_FRAME_IS_DIRTY);
1146 InvalidateFrameSubtree();
1147 PropagateIsUnderHiddenEmbedderElementToSubView(
1148 PresShell()->IsUnderHiddenEmbedderElement() ||
1149 !StyleVisibility()->IsVisible());
1151 if (weakOther.IsAlive()) {
1152 other->PresShell()->FrameNeedsReflow(other, IntrinsicDirty::TreeChange,
1153 NS_FRAME_IS_DIRTY);
1154 other->InvalidateFrameSubtree();
1155 other->PropagateIsUnderHiddenEmbedderElementToSubView(
1156 other->PresShell()->IsUnderHiddenEmbedderElement() ||
1157 !other->StyleVisibility()->IsVisible());
1161 void nsSubDocumentFrame::ClearDisplayItems() {
1162 for (nsDisplayItem* i : DisplayItems()) {
1163 if (i->GetType() == DisplayItemType::TYPE_SUBDOCUMENT) {
1164 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
1165 MOZ_ASSERT(displayRoot);
1167 RetainedDisplayListBuilder* retainedBuilder =
1168 displayRoot->GetProperty(RetainedDisplayListBuilder::Cached());
1169 MOZ_ASSERT(retainedBuilder);
1171 auto* item = static_cast<nsDisplaySubDocument*>(i);
1172 item->GetChildren()->DeleteAll(retainedBuilder->Builder());
1173 item->Disown();
1174 break;
1179 nsView* nsSubDocumentFrame::EnsureInnerView() {
1180 if (mInnerView) {
1181 return mInnerView;
1184 // create, init, set the parent of the view
1185 nsView* outerView = GetView();
1186 NS_ASSERTION(outerView, "Must have an outer view already");
1187 nsRect viewBounds(0, 0, 0, 0); // size will be fixed during reflow
1189 nsViewManager* viewMan = outerView->GetViewManager();
1190 nsView* innerView = viewMan->CreateView(viewBounds, outerView);
1191 if (!innerView) {
1192 NS_ERROR("Could not create inner view");
1193 return nullptr;
1195 mInnerView = innerView;
1196 viewMan->InsertChild(outerView, innerView, nullptr, true);
1198 return mInnerView;
1201 nsPoint nsSubDocumentFrame::GetExtraOffset() const {
1202 MOZ_ASSERT(mInnerView);
1203 return mInnerView->GetPosition();
1206 void nsSubDocumentFrame::SubdocumentIntrinsicSizeOrRatioChanged() {
1207 if (MOZ_UNLIKELY(HasAllStateBits(NS_FRAME_IS_DIRTY))) {
1208 // We will be reflowed soon anyway.
1209 return;
1212 const nsStylePosition* pos = StylePosition();
1213 bool dependsOnIntrinsics =
1214 !pos->mWidth.ConvertsToLength() || !pos->mHeight.ConvertsToLength();
1216 if (dependsOnIntrinsics || pos->mObjectFit != StyleObjectFit::Fill) {
1217 auto dirtyHint = dependsOnIntrinsics ? IntrinsicDirty::StyleChange
1218 : IntrinsicDirty::Resize;
1219 PresShell()->FrameNeedsReflow(this, dirtyHint, NS_FRAME_IS_DIRTY);
1224 * Gets the layer-pixel offset of aContainerFrame's content rect top-left
1225 * from the nearest display item reference frame (which we assume will be
1226 * inducing a ContainerLayer).
1228 static nsPoint GetContentRectLayerOffset(nsIFrame* aContainerFrame,
1229 nsDisplayListBuilder* aBuilder) {
1230 // Offset to the content rect in case we have borders or padding
1231 // Note that aContainerFrame could be a reference frame itself, so
1232 // we need to be careful here to ensure that we call ToReferenceFrame
1233 // on aContainerFrame and not its parent.
1234 nsPoint frameOffset =
1235 aBuilder->ToReferenceFrame(aContainerFrame) +
1236 aContainerFrame->GetContentRectRelativeToSelf().TopLeft();
1238 return frameOffset;
1241 // Return true iff |aManager| is a "temporary layer manager". They're
1242 // used for small software rendering tasks, like drawWindow. That's
1243 // currently implemented by a BasicLayerManager without a backing
1244 // widget, and hence in non-retained mode.
1245 nsDisplayRemote::nsDisplayRemote(nsDisplayListBuilder* aBuilder,
1246 nsSubDocumentFrame* aFrame)
1247 : nsPaintedDisplayItem(aBuilder, aFrame),
1248 mEventRegionsOverride(EventRegionsOverride::NoOverride) {
1249 const bool frameIsPointerEventsNone =
1250 aFrame->Style()->PointerEvents() == StylePointerEvents::None;
1251 if (aBuilder->IsInsidePointerEventsNoneDoc() || frameIsPointerEventsNone) {
1252 mEventRegionsOverride |= EventRegionsOverride::ForceEmptyHitRegion;
1254 if (nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(
1255 aFrame->PresShell())) {
1256 mEventRegionsOverride |= EventRegionsOverride::ForceDispatchToContent;
1259 mPaintData = aFrame->GetRemotePaintData();
1262 LayerIntSize GetFrameSize(const nsIFrame* aFrame) {
1263 LayoutDeviceSize size = LayoutDeviceRect::FromAppUnits(
1264 aFrame->GetContentRectRelativeToSelf().Size(),
1265 aFrame->PresContext()->AppUnitsPerDevPixel());
1267 return LayerIntSize::Round(size.width, size.height);
1270 namespace mozilla {
1272 void nsDisplayRemote::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
1273 nsPresContext* pc = mFrame->PresContext();
1274 nsFrameLoader* fl = GetFrameLoader();
1275 if (pc->GetPrintSettings() && fl->IsRemoteFrame()) {
1276 // See the comment below in CreateWebRenderCommands() as for why doing this.
1277 fl->UpdatePositionAndSize(static_cast<nsSubDocumentFrame*>(mFrame));
1280 DrawTarget* target = aCtx->GetDrawTarget();
1281 if (!target->IsRecording() || mPaintData.mTabId == 0) {
1282 NS_WARNING("Remote iframe not rendered");
1283 return;
1286 // Rendering the inner document will apply a scale to account for its
1287 // app units per dev pixel ratio. We want to apply the inverse scaling
1288 // using our app units per dev pixel ratio, so that no actual scaling
1289 // will be applied if they match. For in-process rendering,
1290 // nsSubDocumentFrame creates an nsDisplayZoom item if the app units
1291 // per dev pixel ratio changes.
1292 int32_t appUnitsPerDevPixel = pc->AppUnitsPerDevPixel();
1293 gfxFloat scale = gfxFloat(AppUnitsPerCSSPixel()) / appUnitsPerDevPixel;
1294 gfxContextMatrixAutoSaveRestore saveMatrix(aCtx);
1295 aCtx->Multiply(gfxMatrix::Scaling(scale, scale));
1297 Rect destRect =
1298 NSRectToSnappedRect(GetContentRect(), AppUnitsPerCSSPixel(), *target);
1299 target->DrawDependentSurface(mPaintData.mTabId, destRect);
1302 bool nsDisplayRemote::CreateWebRenderCommands(
1303 mozilla::wr::DisplayListBuilder& aBuilder,
1304 mozilla::wr::IpcResourceUpdateQueue& aResources,
1305 const StackingContextHelper& aSc,
1306 mozilla::layers::RenderRootStateManager* aManager,
1307 nsDisplayListBuilder* aDisplayListBuilder) {
1308 if (!mPaintData.mLayersId.IsValid()) {
1309 return true;
1312 nsPresContext* pc = mFrame->PresContext();
1313 nsFrameLoader* fl = GetFrameLoader();
1314 if (RefPtr<RemoteBrowser> remoteBrowser = fl->GetRemoteBrowser()) {
1315 if (pc->GetPrintSettings()) {
1316 // HACK(emilio): Usually we update sizing/positioning from
1317 // ReflowFinished(). Print documents have no incremental reflow at all
1318 // though, so we can't rely on it firing after a frame becomes remote.
1319 // Thus, if we're painting a remote frame, update its sizing and position
1320 // now.
1322 // UpdatePositionAndSize() can cause havoc for non-remote frames but
1323 // luckily we don't care about those, so this is fine.
1324 fl->UpdatePositionAndSize(static_cast<nsSubDocumentFrame*>(mFrame));
1327 // Adjust mItemVisibleRect, which is relative to the reference frame, to be
1328 // relative to this frame
1329 nsRect visibleRect = GetBuildingRect() - ToReferenceFrame();
1330 nsRect contentRect = Frame()->GetContentRectRelativeToSelf();
1331 visibleRect.IntersectRect(visibleRect, contentRect);
1332 visibleRect -= contentRect.TopLeft();
1334 // Generate an effects update notifying the browser it is visible
1335 gfx::Size scale = aSc.GetInheritedScale();
1337 ParentLayerToScreenScale2D transformToAncestorScale =
1338 ParentLayerToParentLayerScale(
1339 pc->GetPresShell() ? pc->GetPresShell()->GetCumulativeResolution()
1340 : 1.f) *
1341 nsLayoutUtils::GetTransformToAncestorScaleCrossProcessForFrameMetrics(
1342 mFrame);
1344 aDisplayListBuilder->AddEffectUpdate(
1345 remoteBrowser,
1346 EffectsInfo::VisibleWithinRect(visibleRect, scale.width, scale.height,
1347 transformToAncestorScale));
1349 // Create a WebRenderRemoteData to notify the RemoteBrowser when it is no
1350 // longer visible
1351 RefPtr<WebRenderRemoteData> userData =
1352 aManager->CommandBuilder()
1353 .CreateOrRecycleWebRenderUserData<WebRenderRemoteData>(this,
1354 nullptr);
1355 userData->SetRemoteBrowser(remoteBrowser);
1358 nscoord auPerDevPixel = pc->AppUnitsPerDevPixel();
1359 nsPoint layerOffset = GetContentRectLayerOffset(mFrame, aDisplayListBuilder);
1360 mOffset = LayoutDevicePoint::FromAppUnits(layerOffset, auPerDevPixel);
1362 nsRect contentRect = mFrame->GetContentRectRelativeToSelf();
1363 contentRect.MoveTo(0, 0);
1364 LayoutDeviceRect rect =
1365 LayoutDeviceRect::FromAppUnits(contentRect, auPerDevPixel);
1366 rect += mOffset;
1368 aBuilder.PushIFrame(mozilla::wr::ToLayoutRect(rect), !BackfaceIsHidden(),
1369 mozilla::wr::AsPipelineId(mPaintData.mLayersId),
1370 /*ignoreMissingPipelines*/ true);
1372 return true;
1375 bool nsDisplayRemote::UpdateScrollData(
1376 mozilla::layers::WebRenderScrollData* aData,
1377 mozilla::layers::WebRenderLayerScrollData* aLayerData) {
1378 if (!mPaintData.mLayersId.IsValid()) {
1379 return true;
1382 if (aLayerData) {
1383 aLayerData->SetReferentId(mPaintData.mLayersId);
1385 // Apply the top level resolution if we are in the same process of the top
1386 // level document. We don't need to apply it in cases where we are in OOP
1387 // iframes since it will be applied later in
1388 // HitTestingTreeNode::GetTransformToGecko by walking up the tree node.
1389 nsPresContext* inProcessRootContext =
1390 mFrame->PresContext()->GetInProcessRootContentDocumentPresContext();
1391 if (inProcessRootContext &&
1392 inProcessRootContext->IsRootContentDocumentCrossProcess()) {
1393 float resolution = inProcessRootContext->PresShell()->GetResolution();
1394 aLayerData->SetResolution(resolution);
1397 Matrix4x4 m = Matrix4x4::Translation(mOffset.x, mOffset.y, 0.0);
1398 aLayerData->SetTransform(m);
1399 aLayerData->SetEventRegionsOverride(mEventRegionsOverride);
1400 aLayerData->SetRemoteDocumentSize(GetFrameSize(mFrame));
1402 return true;
1405 nsFrameLoader* nsDisplayRemote::GetFrameLoader() const {
1406 return static_cast<nsSubDocumentFrame*>(mFrame)->FrameLoader();
1409 } // namespace mozilla