Bug 1879774 [wpt PR 44524] - WebKit export: Implement field-sizing support for input...
[gecko.git] / layout / generic / nsSubDocumentFrame.cpp
blobbfbd7763a9b19c8c011d50dbd62cb58b6cf51169
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/ComputedStyleInlines.h"
15 #include "mozilla/Preferences.h"
16 #include "mozilla/PresShell.h"
17 #include "mozilla/StaticPrefs_layout.h"
18 #include "mozilla/Unused.h"
19 #include "mozilla/dom/Document.h"
20 #include "mozilla/dom/HTMLFrameElement.h"
21 #include "mozilla/dom/ImageDocument.h"
22 #include "mozilla/dom/BrowserParent.h"
24 #include "nsCOMPtr.h"
25 #include "nsGenericHTMLElement.h"
26 #include "nsGenericHTMLFrameElement.h"
27 #include "nsAttrValueInlines.h"
28 #include "nsIDocShell.h"
29 #include "nsIDocumentViewer.h"
30 #include "nsIContentInlines.h"
31 #include "nsPresContext.h"
32 #include "nsView.h"
33 #include "nsViewManager.h"
34 #include "nsGkAtoms.h"
35 #include "nsStyleConsts.h"
36 #include "nsStyleStruct.h"
37 #include "nsStyleStructInlines.h"
38 #include "nsFrameSetFrame.h"
39 #include "nsNameSpaceManager.h"
40 #include "nsDisplayList.h"
41 #include "nsIScrollableFrame.h"
42 #include "nsIObjectLoadingContent.h"
43 #include "nsLayoutUtils.h"
44 #include "nsContentUtils.h"
45 #include "nsServiceManagerUtils.h"
46 #include "nsQueryObject.h"
47 #include "RetainedDisplayListBuilder.h"
48 #include "nsObjectLoadingContent.h"
50 #include "mozilla/layers/WebRenderUserData.h"
51 #include "mozilla/layers/WebRenderScrollData.h"
52 #include "mozilla/layers/RenderRootStateManager.h"
53 #include "mozilla/layers/StackingContextHelper.h" // for StackingContextHelper
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 static void PropagateIsUnderHiddenEmbedderElement(nsFrameLoader* aFrameLoader,
70 bool aValue) {
71 if (!aFrameLoader) {
72 return;
75 if (BrowsingContext* bc = aFrameLoader->GetExtantBrowsingContext()) {
76 if (bc->IsUnderHiddenEmbedderElement() != aValue) {
77 Unused << bc->SetIsUnderHiddenEmbedderElement(aValue);
82 nsSubDocumentFrame::nsSubDocumentFrame(ComputedStyle* aStyle,
83 nsPresContext* aPresContext)
84 : nsAtomicContainerFrame(aStyle, aPresContext, kClassID),
85 mOuterView(nullptr),
86 mInnerView(nullptr),
87 mIsInline(false),
88 mPostedReflowCallback(false),
89 mDidCreateDoc(false),
90 mCallingShow(false),
91 mIsInObjectOrEmbed(false) {}
93 #ifdef ACCESSIBILITY
94 a11y::AccType nsSubDocumentFrame::AccessibleType() {
95 return a11y::eOuterDocType;
97 #endif
99 NS_QUERYFRAME_HEAD(nsSubDocumentFrame)
100 NS_QUERYFRAME_ENTRY(nsSubDocumentFrame)
101 NS_QUERYFRAME_TAIL_INHERITING(nsAtomicContainerFrame)
103 class AsyncFrameInit : public Runnable {
104 public:
105 explicit AsyncFrameInit(nsIFrame* aFrame)
106 : mozilla::Runnable("AsyncFrameInit"), mFrame(aFrame) {}
107 NS_IMETHOD Run() override {
108 AUTO_PROFILER_LABEL("AsyncFrameInit::Run", OTHER);
109 if (mFrame.IsAlive()) {
110 static_cast<nsSubDocumentFrame*>(mFrame.GetFrame())->ShowViewer();
112 return NS_OK;
115 private:
116 WeakFrame mFrame;
119 void nsSubDocumentFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
120 nsIFrame* aPrevInFlow) {
121 MOZ_ASSERT(aContent);
122 // determine if we are a <frame> or <iframe>
123 mIsInline = !aContent->IsHTMLElement(nsGkAtoms::frame);
125 nsAtomicContainerFrame::Init(aContent, aParent, aPrevInFlow);
127 // CreateView() creates this frame's view, stored in mOuterView. It needs to
128 // be created first since it's the parent of the inner view, stored in
129 // mInnerView.
130 CreateView();
131 EnsureInnerView();
133 // Set the primary frame now so that nsDocumentViewer::FindContainerView
134 // called from within EndSwapDocShellsForViews below can find it if needed.
135 aContent->SetPrimaryFrame(this);
137 // If we have a detached subdoc's root view on our frame loader, re-insert it
138 // into the view tree. This happens when we've been reframed, and ensures the
139 // presentation persists across reframes.
140 if (RefPtr<nsFrameLoader> frameloader = FrameLoader()) {
141 bool hadFrame = false;
142 nsIFrame* detachedFrame = frameloader->GetDetachedSubdocFrame(&hadFrame);
143 frameloader->SetDetachedSubdocFrame(nullptr);
144 nsView* detachedView = detachedFrame ? detachedFrame->GetView() : nullptr;
145 if (detachedView) {
146 // Restore stashed presentation.
147 InsertViewsInReverseOrder(detachedView, mInnerView);
148 EndSwapDocShellsForViews(mInnerView->GetFirstChild());
149 } else if (hadFrame) {
150 // Presentation is for a different document, don't restore it.
151 frameloader->Hide();
155 // NOTE: The frame loader might not yet be initialized yet. If it's not, the
156 // call in ShowViewer() should pick things up.
157 UpdateEmbeddedBrowsingContextDependentData();
158 nsContentUtils::AddScriptRunner(new AsyncFrameInit(this));
161 void nsSubDocumentFrame::UpdateEmbeddedBrowsingContextDependentData() {
162 if (!mFrameLoader) {
163 return;
165 BrowsingContext* bc = mFrameLoader->GetExtantBrowsingContext();
166 if (!bc) {
167 return;
169 mIsInObjectOrEmbed = bc->IsEmbedderTypeObjectOrEmbed();
170 MaybeUpdateRemoteStyle();
171 MaybeUpdateEmbedderColorScheme();
172 PropagateIsUnderHiddenEmbedderElement(
173 PresShell()->IsUnderHiddenEmbedderElement() ||
174 !StyleVisibility()->IsVisible());
177 void nsSubDocumentFrame::PropagateIsUnderHiddenEmbedderElement(bool aValue) {
178 ::PropagateIsUnderHiddenEmbedderElement(mFrameLoader, aValue);
181 void nsSubDocumentFrame::ShowViewer() {
182 if (mCallingShow) {
183 return;
186 RefPtr<nsFrameLoader> frameloader = FrameLoader();
187 if (!frameloader || frameloader->IsDead()) {
188 return;
191 if (!frameloader->IsRemoteFrame() && !PresContext()->IsDynamic()) {
192 // We let the printing code take care of loading the document and
193 // initializing the shell; just create the inner view for it to use.
194 (void)EnsureInnerView();
195 } else {
196 AutoWeakFrame weakThis(this);
197 mCallingShow = true;
198 bool didCreateDoc = frameloader->Show(this);
199 if (!weakThis.IsAlive()) {
200 return;
202 mCallingShow = false;
203 mDidCreateDoc = didCreateDoc;
204 if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
205 frameloader->UpdatePositionAndSize(this);
207 if (!weakThis.IsAlive()) {
208 return;
210 UpdateEmbeddedBrowsingContextDependentData();
211 InvalidateFrame();
215 nsIFrame* nsSubDocumentFrame::GetSubdocumentRootFrame() {
216 if (!mInnerView) return nullptr;
217 nsView* subdocView = mInnerView->GetFirstChild();
218 return subdocView ? subdocView->GetFrame() : nullptr;
221 mozilla::PresShell* nsSubDocumentFrame::GetSubdocumentPresShellForPainting(
222 uint32_t aFlags) {
223 if (!mInnerView) return nullptr;
225 nsView* subdocView = mInnerView->GetFirstChild();
226 if (!subdocView) return nullptr;
228 mozilla::PresShell* presShell = nullptr;
230 nsIFrame* subdocRootFrame = subdocView->GetFrame();
231 if (subdocRootFrame) {
232 presShell = subdocRootFrame->PresShell();
235 // If painting is suppressed in the presshell, we try to look for a better
236 // presshell to use.
237 if (!presShell || (presShell->IsPaintingSuppressed() &&
238 !(aFlags & IGNORE_PAINT_SUPPRESSION))) {
239 // During page transition mInnerView will sometimes have two children, the
240 // first being the new page that may not have any frame, and the second
241 // being the old page that will probably have a frame.
242 nsView* nextView = subdocView->GetNextSibling();
243 nsIFrame* frame = nullptr;
244 if (nextView) {
245 frame = nextView->GetFrame();
247 if (frame) {
248 mozilla::PresShell* presShellForNextView = frame->PresShell();
249 if (!presShell || (presShellForNextView &&
250 !presShellForNextView->IsPaintingSuppressed() &&
251 StaticPrefs::layout_show_previous_page())) {
252 subdocView = nextView;
253 subdocRootFrame = frame;
254 presShell = presShellForNextView;
257 if (!presShell) {
258 // If we don't have a frame we use this roundabout way to get the pres
259 // shell.
260 if (!mFrameLoader) return nullptr;
261 nsIDocShell* docShell = mFrameLoader->GetDocShell(IgnoreErrors());
262 if (!docShell) return nullptr;
263 presShell = docShell->GetPresShell();
267 return presShell;
270 nsRect nsSubDocumentFrame::GetDestRect() {
271 nsRect rect = GetContent()->IsHTMLElement(nsGkAtoms::frame)
272 ? GetRectRelativeToSelf()
273 : GetContentRectRelativeToSelf();
275 // Adjust subdocument size, according to 'object-fit' and the subdocument's
276 // intrinsic size and ratio.
277 return nsLayoutUtils::ComputeObjectDestRect(
278 rect, GetIntrinsicSize(), GetIntrinsicRatio(), StylePosition());
281 ScreenIntSize nsSubDocumentFrame::GetSubdocumentSize() {
282 if (HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
283 if (RefPtr<nsFrameLoader> frameloader = FrameLoader()) {
284 nsIFrame* detachedFrame = frameloader->GetDetachedSubdocFrame();
285 if (nsView* view = detachedFrame ? detachedFrame->GetView() : nullptr) {
286 nsSize size = view->GetBounds().Size();
287 nsPresContext* presContext = detachedFrame->PresContext();
288 return ScreenIntSize(presContext->AppUnitsToDevPixels(size.width),
289 presContext->AppUnitsToDevPixels(size.height));
292 // Pick some default size for now. Using 10x10 because that's what the
293 // code used to do.
294 return ScreenIntSize(10, 10);
297 nsSize docSizeAppUnits = GetDestRect().Size();
298 nsPresContext* pc = PresContext();
299 return ScreenIntSize(pc->AppUnitsToDevPixels(docSizeAppUnits.width),
300 pc->AppUnitsToDevPixels(docSizeAppUnits.height));
303 static void WrapBackgroundColorInOwnLayer(nsDisplayListBuilder* aBuilder,
304 nsIFrame* aFrame,
305 nsDisplayList* aList) {
306 for (nsDisplayItem* item : aList->TakeItems()) {
307 if (item->GetType() == DisplayItemType::TYPE_BACKGROUND_COLOR) {
308 nsDisplayList tmpList(aBuilder);
309 tmpList.AppendToTop(item);
310 item = MakeDisplayItemWithIndex<nsDisplayOwnLayer>(
311 aBuilder, aFrame, /* aIndex = */ nsDisplayOwnLayer::OwnLayerForSubdoc,
312 &tmpList, aBuilder->CurrentActiveScrolledRoot(),
313 nsDisplayOwnLayerFlags::None, ScrollbarData{}, true, false);
315 aList->AppendToTop(item);
319 void nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
320 const nsDisplayListSet& aLists) {
321 if (!IsVisibleForPainting()) {
322 return;
325 nsFrameLoader* frameLoader = FrameLoader();
326 bool isRemoteFrame = frameLoader && frameLoader->IsRemoteFrame();
328 // If we are pointer-events:none then we don't need to HitTest background
329 const bool pointerEventsNone =
330 Style()->PointerEvents() == StylePointerEvents::None;
331 if (!aBuilder->IsForEventDelivery() || !pointerEventsNone) {
332 nsDisplayListCollection decorations(aBuilder);
333 DisplayBorderBackgroundOutline(aBuilder, decorations);
334 if (isRemoteFrame) {
335 // Wrap background colors of <iframe>s with remote subdocuments in their
336 // own layer so we generate a ColorLayer. This is helpful for optimizing
337 // compositing; we can skip compositing the ColorLayer when the
338 // remote content is opaque.
339 WrapBackgroundColorInOwnLayer(aBuilder, this,
340 decorations.BorderBackground());
342 decorations.MoveTo(aLists);
345 if (aBuilder->IsForEventDelivery() && pointerEventsNone) {
346 return;
349 if (HidesContent()) {
350 return;
353 // If we're passing pointer events to children then we have to descend into
354 // subdocuments no matter what, to determine which parts are transparent for
355 // hit-testing or event regions.
356 bool needToDescend = aBuilder->GetDescendIntoSubdocuments();
357 if (!mInnerView || !needToDescend) {
358 return;
361 if (isRemoteFrame) {
362 // We're the subdoc for <browser remote="true"> and it has
363 // painted content. Display its shadow layer tree.
364 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
365 clipState.ClipContainingBlockDescendantsToContentBox(aBuilder, this);
367 aLists.Content()->AppendNewToTop<nsDisplayRemote>(aBuilder, this);
368 return;
371 RefPtr<mozilla::PresShell> presShell = GetSubdocumentPresShellForPainting(
372 aBuilder->IsIgnoringPaintSuppression() ? IGNORE_PAINT_SUPPRESSION : 0);
374 if (!presShell) {
375 return;
378 if (aBuilder->IsInFilter()) {
379 Document* outerDoc = PresShell()->GetDocument();
380 Document* innerDoc = presShell->GetDocument();
381 if (outerDoc && innerDoc) {
382 if (!outerDoc->NodePrincipal()->Equals(innerDoc->NodePrincipal())) {
383 outerDoc->SetUseCounter(eUseCounter_custom_FilteredCrossOriginIFrame);
388 nsIFrame* subdocRootFrame = presShell->GetRootFrame();
390 nsPresContext* presContext = presShell->GetPresContext();
392 int32_t parentAPD = PresContext()->AppUnitsPerDevPixel();
393 int32_t subdocAPD = presContext->AppUnitsPerDevPixel();
395 nsRect visible;
396 nsRect dirty;
397 bool ignoreViewportScrolling = false;
398 if (subdocRootFrame) {
399 // get the dirty rect relative to the root frame of the subdoc
400 visible = aBuilder->GetVisibleRect() + GetOffsetToCrossDoc(subdocRootFrame);
401 dirty = aBuilder->GetDirtyRect() + GetOffsetToCrossDoc(subdocRootFrame);
402 // and convert into the appunits of the subdoc
403 visible = visible.ScaleToOtherAppUnitsRoundOut(parentAPD, subdocAPD);
404 dirty = dirty.ScaleToOtherAppUnitsRoundOut(parentAPD, subdocAPD);
406 if (nsIScrollableFrame* rootScrollableFrame =
407 presShell->GetRootScrollFrameAsScrollable()) {
408 // Use a copy, so the rects don't get modified.
409 nsRect copyOfDirty = dirty;
410 nsRect copyOfVisible = visible;
411 // TODO(botond): Can we just axe this DecideScrollableLayer call?
412 rootScrollableFrame->DecideScrollableLayer(aBuilder, &copyOfVisible,
413 &copyOfDirty,
414 /* aSetBase = */ true);
416 ignoreViewportScrolling = presShell->IgnoringViewportScrolling();
419 aBuilder->EnterPresShell(subdocRootFrame, pointerEventsNone);
420 aBuilder->IncrementPresShellPaintCount(presShell);
421 } else {
422 visible = aBuilder->GetVisibleRect();
423 dirty = aBuilder->GetDirtyRect();
426 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
427 clipState.ClipContainingBlockDescendantsToContentBox(aBuilder, this);
429 nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
430 bool constructZoomItem = subdocRootFrame && parentAPD != subdocAPD;
431 bool needsOwnLayer = constructZoomItem ||
432 presContext->IsRootContentDocumentCrossProcess() ||
433 (sf && sf->IsScrollingActive());
435 nsDisplayList childItems(aBuilder);
438 DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
439 if (needsOwnLayer) {
440 // Clear current clip. There's no point in propagating it down, since
441 // the layer we will construct will be clipped by the current clip.
442 // In fact for nsDisplayZoom propagating it down would be incorrect since
443 // nsDisplayZoom changes the meaning of appunits.
444 nestedClipState.Clear();
447 // Invoke AutoBuildingDisplayList to ensure that the correct dirty rect
448 // is used to compute the visible rect if AddCanvasBackgroundColorItem
449 // creates a display item.
450 nsIFrame* frame = subdocRootFrame ? subdocRootFrame : this;
451 nsDisplayListBuilder::AutoBuildingDisplayList building(aBuilder, frame,
452 visible, dirty);
454 if (subdocRootFrame) {
455 bool hasDocumentLevelListenersForApzAwareEvents =
456 gfxPlatform::AsyncPanZoomEnabled() &&
457 nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(presShell);
459 aBuilder->SetAncestorHasApzAwareEventHandler(
460 hasDocumentLevelListenersForApzAwareEvents);
461 subdocRootFrame->BuildDisplayListForStackingContext(aBuilder,
462 &childItems);
463 if (!aBuilder->IsForEventDelivery()) {
464 // If we are going to use a displayzoom below then any items we put
465 // under it need to have underlying frames from the subdocument. So we
466 // need to calculate the bounds based on which frame will be the
467 // underlying frame for the canvas background color item.
468 nsRect bounds =
469 GetContentRectRelativeToSelf() + aBuilder->ToReferenceFrame(this);
470 bounds = bounds.ScaleToOtherAppUnitsRoundOut(parentAPD, subdocAPD);
472 // Add the canvas background color to the bottom of the list. This
473 // happens after we've built the list so that
474 // AddCanvasBackgroundColorItem can monkey with the contents if
475 // necessary.
476 presShell->AddCanvasBackgroundColorItem(aBuilder, &childItems, frame,
477 bounds, NS_RGBA(0, 0, 0, 0));
482 if (subdocRootFrame) {
483 aBuilder->LeavePresShell(subdocRootFrame, &childItems);
486 // Generate a resolution and/or zoom item if needed. If one or both of those
487 // is created, we don't need to create a separate nsDisplaySubDocument.
489 nsDisplayOwnLayerFlags flags =
490 nsDisplayOwnLayerFlags::GenerateSubdocInvalidations;
491 // If ignoreViewportScrolling is true then the top most layer we create here
492 // is going to become the scrollable layer for the root scroll frame, so we
493 // want to add nsDisplayOwnLayer::GENERATE_SCROLLABLE_LAYER to whatever layer
494 // becomes the topmost. We do this below.
495 if (constructZoomItem) {
496 nsDisplayOwnLayerFlags zoomFlags = flags;
497 if (ignoreViewportScrolling) {
498 zoomFlags |= nsDisplayOwnLayerFlags::GenerateScrollableLayer;
500 childItems.AppendNewToTop<nsDisplayZoom>(aBuilder, subdocRootFrame, this,
501 &childItems, subdocAPD, parentAPD,
502 zoomFlags);
504 needsOwnLayer = false;
506 // Wrap the zoom item in the resolution item if we have both because we want
507 // the resolution scale applied on top of the app units per dev pixel
508 // conversion.
509 if (ignoreViewportScrolling) {
510 flags |= nsDisplayOwnLayerFlags::GenerateScrollableLayer;
513 // We always want top level content documents to be in their own layer.
514 nsDisplaySubDocument* layerItem = MakeDisplayItem<nsDisplaySubDocument>(
515 aBuilder, subdocRootFrame ? subdocRootFrame : this, this, &childItems,
516 flags);
517 if (layerItem) {
518 childItems.AppendToTop(layerItem);
519 layerItem->SetShouldFlattenAway(!needsOwnLayer);
522 if (aBuilder->IsForFrameVisibility()) {
523 // We don't add the childItems to the return list as we're dealing with them
524 // here.
525 presShell->RebuildApproximateFrameVisibilityDisplayList(childItems);
526 childItems.DeleteAll(aBuilder);
527 } else {
528 aLists.Content()->AppendToTop(&childItems);
532 #ifdef DEBUG_FRAME_DUMP
533 void nsSubDocumentFrame::List(FILE* out, const char* aPrefix,
534 ListFlags aFlags) const {
535 nsCString str;
536 ListGeneric(str, aPrefix, aFlags);
537 fprintf_stderr(out, "%s\n", str.get());
539 if (aFlags.contains(ListFlag::TraverseSubdocumentFrames)) {
540 nsSubDocumentFrame* f = const_cast<nsSubDocumentFrame*>(this);
541 nsIFrame* subdocRootFrame = f->GetSubdocumentRootFrame();
542 if (subdocRootFrame) {
543 nsCString pfx(aPrefix);
544 pfx += " ";
545 subdocRootFrame->List(out, pfx.get(), aFlags);
550 nsresult nsSubDocumentFrame::GetFrameName(nsAString& aResult) const {
551 return MakeFrameName(u"FrameOuter"_ns, aResult);
553 #endif
555 /* virtual */
556 nscoord nsSubDocumentFrame::GetMinISize(gfxContext* aRenderingContext) {
557 nscoord result;
558 DISPLAY_MIN_INLINE_SIZE(this, result);
560 nsCOMPtr<nsIObjectLoadingContent> iolc = do_QueryInterface(mContent);
561 auto olc = static_cast<nsObjectLoadingContent*>(iolc.get());
563 if (olc && olc->GetSubdocumentIntrinsicSize()) {
564 // The subdocument is an SVG document, so technically we should call
565 // SVGOuterSVGFrame::GetMinISize() on its root frame. That method always
566 // returns 0, though, so we can just do that & don't need to bother with
567 // the cross-doc communication.
568 result = 0;
569 } else {
570 result = GetIntrinsicISize();
573 return result;
576 /* virtual */
577 nscoord nsSubDocumentFrame::GetPrefISize(gfxContext* aRenderingContext) {
578 nscoord result;
579 DISPLAY_PREF_INLINE_SIZE(this, result);
581 // If the subdocument is an SVG document, then in theory we want to return
582 // the same thing that SVGOuterSVGFrame::GetPrefISize does. That method
583 // has some special handling of percentage values to avoid unhelpful zero
584 // sizing in the presence of orthogonal writing modes. We don't bother
585 // with that for SVG documents in <embed> and <object>, since that special
586 // handling doesn't look up across document boundaries anyway.
587 result = GetIntrinsicISize();
589 return result;
592 /* virtual */
593 IntrinsicSize nsSubDocumentFrame::GetIntrinsicSize() {
594 const auto containAxes = GetContainSizeAxes();
595 if (containAxes.IsBoth()) {
596 // Intrinsic size of 'contain:size' replaced elements is determined by
597 // contain-intrinsic-size.
598 return FinishIntrinsicSize(containAxes, IntrinsicSize(0, 0));
601 if (nsCOMPtr<nsIObjectLoadingContent> iolc = do_QueryInterface(mContent)) {
602 const auto* olc = static_cast<nsObjectLoadingContent*>(iolc.get());
603 if (auto size = olc->GetSubdocumentIntrinsicSize()) {
604 // Use the intrinsic size from the child SVG document, if available.
605 return FinishIntrinsicSize(containAxes, *size);
609 if (!IsInline()) {
610 return {}; // <frame> elements have no useful intrinsic size.
613 if (mContent->IsXULElement()) {
614 return {}; // XUL <iframe> and <browser> have no useful intrinsic size
617 // We must be an HTML <iframe>. Return fallback size.
618 return FinishIntrinsicSize(containAxes,
619 IntrinsicSize(kFallbackIntrinsicSize));
622 /* virtual */
623 AspectRatio nsSubDocumentFrame::GetIntrinsicRatio() const {
624 // FIXME(emilio): This should probably respect contain: size and return no
625 // ratio in the case subDocRoot is non-null. Otherwise we do it by virtue of
626 // using a zero-size below and reusing GetIntrinsicSize().
627 if (nsCOMPtr<nsIObjectLoadingContent> iolc = do_QueryInterface(mContent)) {
628 auto olc = static_cast<nsObjectLoadingContent*>(iolc.get());
630 auto ratio = olc->GetSubdocumentIntrinsicRatio();
631 if (ratio && *ratio) {
632 // Use the intrinsic aspect ratio from the child SVG document, if
633 // available.
634 return *ratio;
638 // NOTE(emilio): Even though we have an intrinsic size, we may not have an
639 // intrinsic ratio. For example `<iframe style="width: 100px">` should not
640 // shrink in the vertical axis to preserve the 300x150 ratio.
641 return nsAtomicContainerFrame::GetIntrinsicRatio();
644 /* virtual */
645 LogicalSize nsSubDocumentFrame::ComputeAutoSize(
646 gfxContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize,
647 nscoord aAvailableISize, const LogicalSize& aMargin,
648 const LogicalSize& aBorderPadding, const StyleSizeOverrides& aSizeOverrides,
649 ComputeSizeFlags aFlags) {
650 if (!IsInline()) {
651 return nsIFrame::ComputeAutoSize(aRenderingContext, aWM, aCBSize,
652 aAvailableISize, aMargin, aBorderPadding,
653 aSizeOverrides, aFlags);
656 const WritingMode wm = GetWritingMode();
657 LogicalSize result(wm, GetIntrinsicISize(), GetIntrinsicBSize());
658 return result.ConvertTo(aWM, wm);
661 /* virtual */
662 nsIFrame::SizeComputationResult nsSubDocumentFrame::ComputeSize(
663 gfxContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize,
664 nscoord aAvailableISize, const LogicalSize& aMargin,
665 const LogicalSize& aBorderPadding, const StyleSizeOverrides& aSizeOverrides,
666 ComputeSizeFlags aFlags) {
667 return {ComputeSizeWithIntrinsicDimensions(
668 aRenderingContext, aWM, GetIntrinsicSize(), GetAspectRatio(),
669 aCBSize, aMargin, aBorderPadding, aSizeOverrides, aFlags),
670 AspectRatioUsage::None};
673 void nsSubDocumentFrame::Reflow(nsPresContext* aPresContext,
674 ReflowOutput& aDesiredSize,
675 const ReflowInput& aReflowInput,
676 nsReflowStatus& aStatus) {
677 MarkInReflow();
678 DO_GLOBAL_REFLOW_COUNT("nsSubDocumentFrame");
679 DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
680 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
681 NS_FRAME_TRACE(
682 NS_FRAME_TRACE_CALLS,
683 ("enter nsSubDocumentFrame::Reflow: maxSize=%d,%d",
684 aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()));
686 NS_ASSERTION(aReflowInput.ComputedISize() != NS_UNCONSTRAINEDSIZE,
687 "Shouldn't have unconstrained inline-size here "
688 "thanks to the rules of reflow");
689 NS_ASSERTION(aReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE,
690 "Shouldn't have unconstrained block-size here "
691 "thanks to ComputeAutoSize");
693 NS_ASSERTION(mContent->GetPrimaryFrame() == this, "Shouldn't happen");
695 // XUL <iframe> or <browser>, or HTML <iframe>, <object> or <embed>
696 const auto wm = aReflowInput.GetWritingMode();
697 aDesiredSize.SetSize(wm, aReflowInput.ComputedSizeWithBorderPadding(wm));
699 // "offset" is the offset of our content area from our frame's
700 // top-left corner.
701 nsPoint offset = nsPoint(aReflowInput.ComputedPhysicalBorderPadding().left,
702 aReflowInput.ComputedPhysicalBorderPadding().top);
704 if (mInnerView) {
705 const nsMargin& bp = aReflowInput.ComputedPhysicalBorderPadding();
706 nsSize innerSize(aDesiredSize.Width() - bp.LeftRight(),
707 aDesiredSize.Height() - bp.TopBottom());
709 // Size & position the view according to 'object-fit' & 'object-position'.
710 nsRect destRect = nsLayoutUtils::ComputeObjectDestRect(
711 nsRect(offset, innerSize), GetIntrinsicSize(), GetIntrinsicRatio(),
712 StylePosition());
714 nsViewManager* vm = mInnerView->GetViewManager();
715 vm->MoveViewTo(mInnerView, destRect.x, destRect.y);
716 vm->ResizeView(mInnerView, nsRect(nsPoint(0, 0), destRect.Size()), true);
719 aDesiredSize.SetOverflowAreasToDesiredBounds();
721 FinishAndStoreOverflow(&aDesiredSize);
723 if (!aPresContext->IsRootPaginatedDocument() && !mPostedReflowCallback) {
724 PresShell()->PostReflowCallback(this);
725 mPostedReflowCallback = true;
728 NS_FRAME_TRACE(
729 NS_FRAME_TRACE_CALLS,
730 ("exit nsSubDocumentFrame::Reflow: size=%d,%d status=%s",
731 aDesiredSize.Width(), aDesiredSize.Height(), ToString(aStatus).c_str()));
734 bool nsSubDocumentFrame::ReflowFinished() {
735 RefPtr<nsFrameLoader> frameloader = FrameLoader();
736 if (frameloader) {
737 AutoWeakFrame weakFrame(this);
739 frameloader->UpdatePositionAndSize(this);
741 if (weakFrame.IsAlive()) {
742 // Make sure that we can post a reflow callback in the future.
743 mPostedReflowCallback = false;
745 } else {
746 mPostedReflowCallback = false;
748 return false;
751 void nsSubDocumentFrame::ReflowCallbackCanceled() {
752 mPostedReflowCallback = false;
755 nsresult nsSubDocumentFrame::AttributeChanged(int32_t aNameSpaceID,
756 nsAtom* aAttribute,
757 int32_t aModType) {
758 if (aNameSpaceID != kNameSpaceID_None) {
759 return NS_OK;
762 // If the noResize attribute changes, dis/allow frame to be resized
763 if (aAttribute == nsGkAtoms::noresize) {
764 // Note that we're not doing content type checks, but that's ok -- if
765 // they'd fail we will just end up with a null framesetFrame.
766 if (mContent->GetParent()->IsHTMLElement(nsGkAtoms::frameset)) {
767 nsIFrame* parentFrame = GetParent();
769 if (parentFrame) {
770 // There is no interface for nsHTMLFramesetFrame so QI'ing to
771 // concrete class, yay!
772 nsHTMLFramesetFrame* framesetFrame = do_QueryFrame(parentFrame);
773 if (framesetFrame) {
774 framesetFrame->RecalculateBorderResize();
778 } else if (aAttribute == nsGkAtoms::marginwidth ||
779 aAttribute == nsGkAtoms::marginheight) {
780 // Notify the frameloader
781 if (RefPtr<nsFrameLoader> frameloader = FrameLoader()) {
782 frameloader->MarginsChanged();
786 return NS_OK;
789 void nsSubDocumentFrame::MaybeUpdateEmbedderColorScheme() {
790 nsFrameLoader* fl = mFrameLoader.get();
791 if (!fl) {
792 return;
795 BrowsingContext* bc = fl->GetExtantBrowsingContext();
796 if (!bc) {
797 return;
800 auto ToOverride = [](ColorScheme aScheme) -> PrefersColorSchemeOverride {
801 return aScheme == ColorScheme::Dark ? PrefersColorSchemeOverride::Dark
802 : PrefersColorSchemeOverride::Light;
805 EmbedderColorSchemes schemes{
806 ToOverride(LookAndFeel::ColorSchemeForFrame(this, ColorSchemeMode::Used)),
807 ToOverride(
808 LookAndFeel::ColorSchemeForFrame(this, ColorSchemeMode::Preferred))};
809 if (bc->GetEmbedderColorSchemes() == schemes) {
810 return;
813 Unused << bc->SetEmbedderColorSchemes(schemes);
816 void nsSubDocumentFrame::MaybeUpdateRemoteStyle(
817 ComputedStyle* aOldComputedStyle) {
818 if (!mIsInObjectOrEmbed) {
819 return;
822 if (aOldComputedStyle &&
823 aOldComputedStyle->StyleVisibility()->mImageRendering ==
824 Style()->StyleVisibility()->mImageRendering) {
825 return;
828 if (!mFrameLoader) {
829 return;
832 if (mFrameLoader->IsRemoteFrame()) {
833 mFrameLoader->UpdateRemoteStyle(
834 Style()->StyleVisibility()->mImageRendering);
835 return;
838 BrowsingContext* context = mFrameLoader->GetExtantBrowsingContext();
839 if (!context) {
840 return;
843 Document* document = context->GetDocument();
844 if (!document) {
845 return;
848 if (document->IsImageDocument()) {
849 document->AsImageDocument()->UpdateRemoteStyle(
850 Style()->StyleVisibility()->mImageRendering);
854 void nsSubDocumentFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
855 nsAtomicContainerFrame::DidSetComputedStyle(aOldComputedStyle);
857 MaybeUpdateEmbedderColorScheme();
859 MaybeUpdateRemoteStyle(aOldComputedStyle);
861 // If this presshell has invisible ancestors, we don't need to propagate the
862 // visibility style change to the subdocument since the subdocument should
863 // have already set the IsUnderHiddenEmbedderElement flag in
864 // nsSubDocumentFrame::Init.
865 if (PresShell()->IsUnderHiddenEmbedderElement()) {
866 return;
869 const bool isVisible = StyleVisibility()->IsVisible();
870 if (!aOldComputedStyle ||
871 isVisible != aOldComputedStyle->StyleVisibility()->IsVisible()) {
872 PropagateIsUnderHiddenEmbedderElement(!isVisible);
876 nsIFrame* NS_NewSubDocumentFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
877 return new (aPresShell)
878 nsSubDocumentFrame(aStyle, aPresShell->GetPresContext());
881 NS_IMPL_FRAMEARENA_HELPERS(nsSubDocumentFrame)
883 class nsHideViewer final : public Runnable {
884 public:
885 nsHideViewer(nsIContent* aFrameElement, nsFrameLoader* aFrameLoader,
886 PresShell* aPresShell, bool aHideViewerIfFrameless)
887 : mozilla::Runnable("nsHideViewer"),
888 mFrameElement(aFrameElement),
889 mFrameLoader(aFrameLoader),
890 mPresShell(aPresShell),
891 mHideViewerIfFrameless(aHideViewerIfFrameless) {
892 NS_ASSERTION(mFrameElement, "Must have a frame element");
893 NS_ASSERTION(mFrameLoader, "Must have a frame loader");
894 NS_ASSERTION(mPresShell, "Must have a presshell");
897 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override {
898 // Flush frames, to ensure any pending display:none changes are made.
899 // Note it can be unsafe to flush if we've destroyed the presentation
900 // for some other reason, like if we're shutting down.
902 // But avoid the flush if we know for sure we're away, like when we're out
903 // of the document already.
905 // FIXME(emilio): This could still be a perf footgun when removing lots of
906 // siblings where each of them cause the reframe of an ancestor which happen
907 // to contain a subdocument.
909 // We should find some way to avoid that!
910 if (!mPresShell->IsDestroying() && mFrameElement->IsInComposedDoc()) {
911 mPresShell->FlushPendingNotifications(FlushType::Frames);
914 // Either the frame has been constructed by now, or it never will be,
915 // either way we want to clear the stashed views.
916 mFrameLoader->SetDetachedSubdocFrame(nullptr);
918 nsSubDocumentFrame* frame = do_QueryFrame(mFrameElement->GetPrimaryFrame());
919 if (!frame || frame->FrameLoader() != mFrameLoader) {
920 PropagateIsUnderHiddenEmbedderElement(mFrameLoader, true);
921 if (mHideViewerIfFrameless) {
922 // The frame element has no nsIFrame for the same frame loader.
923 // Hide the nsFrameLoader, which destroys the presentation.
924 mFrameLoader->Hide();
927 return NS_OK;
930 private:
931 const nsCOMPtr<nsIContent> mFrameElement;
932 const RefPtr<nsFrameLoader> mFrameLoader;
933 const RefPtr<PresShell> mPresShell;
934 const bool mHideViewerIfFrameless;
937 static nsView* BeginSwapDocShellsForViews(nsView* aSibling);
939 void nsSubDocumentFrame::Destroy(DestroyContext& aContext) {
940 if (mPostedReflowCallback) {
941 PresShell()->CancelReflowCallback(this);
942 mPostedReflowCallback = false;
945 // Detach the subdocument's views and stash them in the frame loader.
946 // We can then reattach them if we're being reframed (for example if
947 // the frame has been made position:fixed).
948 if (RefPtr<nsFrameLoader> frameloader = FrameLoader()) {
949 ClearDisplayItems();
951 nsView* detachedViews =
952 ::BeginSwapDocShellsForViews(mInnerView->GetFirstChild());
954 frameloader->SetDetachedSubdocFrame(
955 detachedViews ? detachedViews->GetFrame() : nullptr);
957 // We call nsFrameLoader::HideViewer() in a script runner so that we can
958 // safely determine whether the frame is being reframed or destroyed.
959 nsContentUtils::AddScriptRunner(new nsHideViewer(
960 mContent, frameloader, PresShell(), (mDidCreateDoc || mCallingShow)));
963 nsAtomicContainerFrame::Destroy(aContext);
966 nsFrameLoader* nsSubDocumentFrame::FrameLoader() const {
967 if (mFrameLoader) {
968 return mFrameLoader;
971 if (RefPtr<nsFrameLoaderOwner> loaderOwner = do_QueryObject(GetContent())) {
972 mFrameLoader = loaderOwner->GetFrameLoader();
975 return mFrameLoader;
978 auto nsSubDocumentFrame::GetRemotePaintData() const -> RemoteFramePaintData {
979 if (mRetainedRemoteFrame) {
980 return *mRetainedRemoteFrame;
983 RemoteFramePaintData data;
984 nsFrameLoader* fl = FrameLoader();
985 if (!fl) {
986 return data;
989 auto* rb = fl->GetRemoteBrowser();
990 if (!rb) {
991 return data;
993 data.mLayersId = rb->GetLayersId();
994 data.mTabId = rb->GetTabId();
995 return data;
998 void nsSubDocumentFrame::ResetFrameLoader(RetainPaintData aRetain) {
999 if (aRetain == RetainPaintData::Yes && mFrameLoader) {
1000 mRetainedRemoteFrame = Some(GetRemotePaintData());
1001 } else {
1002 mRetainedRemoteFrame.reset();
1004 mFrameLoader = nullptr;
1005 ClearDisplayItems();
1006 nsContentUtils::AddScriptRunner(new AsyncFrameInit(this));
1009 void nsSubDocumentFrame::ClearRetainedPaintData() {
1010 mRetainedRemoteFrame.reset();
1011 ClearDisplayItems();
1012 InvalidateFrameSubtree();
1015 // XXX this should be called ObtainDocShell or something like that,
1016 // to indicate that it could have side effects
1017 nsIDocShell* nsSubDocumentFrame::GetDocShell() const {
1018 // How can FrameLoader() return null???
1019 if (NS_WARN_IF(!FrameLoader())) {
1020 return nullptr;
1022 return mFrameLoader->GetDocShell(IgnoreErrors());
1025 static void DestroyDisplayItemDataForFrames(nsIFrame* aFrame) {
1026 // Destroying a WebRenderUserDataTable can cause destruction of other objects
1027 // which can remove frame properties in their destructor. If we delete a frame
1028 // property it runs the destructor of the stored object in the middle of
1029 // updating the frame property table, so if the destruction of that object
1030 // causes another update to the frame property table it would leave the frame
1031 // property table in an inconsistent state. So we remove it from the table and
1032 // then destroy it. (bug 1530657)
1033 WebRenderUserDataTable* userDataTable =
1034 aFrame->TakeProperty(WebRenderUserDataProperty::Key());
1035 if (userDataTable) {
1036 for (const auto& data : userDataTable->Values()) {
1037 data->RemoveFromTable();
1039 delete userDataTable;
1042 for (const auto& childList : aFrame->ChildLists()) {
1043 for (nsIFrame* child : childList.mList) {
1044 DestroyDisplayItemDataForFrames(child);
1049 static CallState BeginSwapDocShellsForDocument(Document& aDocument) {
1050 if (PresShell* presShell = aDocument.GetPresShell()) {
1051 // Disable painting while the views are detached, see bug 946929.
1052 presShell->SetNeverPainting(true);
1054 if (nsIFrame* rootFrame = presShell->GetRootFrame()) {
1055 ::DestroyDisplayItemDataForFrames(rootFrame);
1058 aDocument.EnumerateSubDocuments(BeginSwapDocShellsForDocument);
1059 return CallState::Continue;
1062 static nsView* BeginSwapDocShellsForViews(nsView* aSibling) {
1063 // Collect the removed sibling views in reverse order in 'removedViews'.
1064 nsView* removedViews = nullptr;
1065 while (aSibling) {
1066 if (Document* doc = ::GetDocumentFromView(aSibling)) {
1067 ::BeginSwapDocShellsForDocument(*doc);
1069 nsView* next = aSibling->GetNextSibling();
1070 aSibling->GetViewManager()->RemoveChild(aSibling);
1071 aSibling->SetNextSibling(removedViews);
1072 removedViews = aSibling;
1073 aSibling = next;
1075 return removedViews;
1078 /* static */
1079 void nsSubDocumentFrame::InsertViewsInReverseOrder(nsView* aSibling,
1080 nsView* aParent) {
1081 MOZ_ASSERT(aParent, "null view");
1082 MOZ_ASSERT(!aParent->GetFirstChild(), "inserting into non-empty list");
1084 nsViewManager* vm = aParent->GetViewManager();
1085 while (aSibling) {
1086 nsView* next = aSibling->GetNextSibling();
1087 aSibling->SetNextSibling(nullptr);
1088 // true means 'after' in document order which is 'before' in view order,
1089 // so this call prepends the child, thus reversing the siblings as we go.
1090 vm->InsertChild(aParent, aSibling, nullptr, true);
1091 aSibling = next;
1095 nsresult nsSubDocumentFrame::BeginSwapDocShells(nsIFrame* aOther) {
1096 if (!aOther || !aOther->IsSubDocumentFrame()) {
1097 return NS_ERROR_NOT_IMPLEMENTED;
1100 nsSubDocumentFrame* other = static_cast<nsSubDocumentFrame*>(aOther);
1101 if (!mFrameLoader || !mDidCreateDoc || mCallingShow || !other->mFrameLoader ||
1102 !other->mDidCreateDoc) {
1103 return NS_ERROR_NOT_IMPLEMENTED;
1106 ClearDisplayItems();
1107 other->ClearDisplayItems();
1109 if (mInnerView && other->mInnerView) {
1110 nsView* ourSubdocViews = mInnerView->GetFirstChild();
1111 nsView* ourRemovedViews = ::BeginSwapDocShellsForViews(ourSubdocViews);
1112 nsView* otherSubdocViews = other->mInnerView->GetFirstChild();
1113 nsView* otherRemovedViews = ::BeginSwapDocShellsForViews(otherSubdocViews);
1115 InsertViewsInReverseOrder(ourRemovedViews, other->mInnerView);
1116 InsertViewsInReverseOrder(otherRemovedViews, mInnerView);
1118 mFrameLoader.swap(other->mFrameLoader);
1119 return NS_OK;
1122 static CallState EndSwapDocShellsForDocument(Document& aDocument) {
1123 // Our docshell and view trees have been updated for the new hierarchy.
1124 // Now also update all nsDeviceContext::mWidget to that of the
1125 // container view in the new hierarchy.
1126 if (nsCOMPtr<nsIDocShell> ds = aDocument.GetDocShell()) {
1127 nsCOMPtr<nsIDocumentViewer> viewer;
1128 ds->GetDocViewer(getter_AddRefs(viewer));
1129 while (viewer) {
1130 RefPtr<nsPresContext> pc = viewer->GetPresContext();
1131 if (pc && pc->GetPresShell()) {
1132 pc->GetPresShell()->SetNeverPainting(ds->IsInvisible());
1134 nsDeviceContext* dc = pc ? pc->DeviceContext() : nullptr;
1135 if (dc) {
1136 nsView* v = viewer->FindContainerView();
1137 dc->Init(v ? v->GetNearestWidget(nullptr) : nullptr);
1139 viewer = viewer->GetPreviousViewer();
1143 aDocument.EnumerateSubDocuments(EndSwapDocShellsForDocument);
1144 return CallState::Continue;
1147 /* static */
1148 void nsSubDocumentFrame::EndSwapDocShellsForViews(nsView* aSibling) {
1149 for (; aSibling; aSibling = aSibling->GetNextSibling()) {
1150 if (Document* doc = ::GetDocumentFromView(aSibling)) {
1151 ::EndSwapDocShellsForDocument(*doc);
1153 nsIFrame* frame = aSibling->GetFrame();
1154 if (frame) {
1155 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame);
1156 if (parent->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
1157 nsIFrame::AddInPopupStateBitToDescendants(frame);
1158 } else {
1159 nsIFrame::RemoveInPopupStateBitFromDescendants(frame);
1161 if (frame->HasInvalidFrameInSubtree()) {
1162 while (parent &&
1163 !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT |
1164 NS_FRAME_IS_NONDISPLAY)) {
1165 parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
1166 parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(parent);
1173 void nsSubDocumentFrame::EndSwapDocShells(nsIFrame* aOther) {
1174 nsSubDocumentFrame* other = static_cast<nsSubDocumentFrame*>(aOther);
1175 AutoWeakFrame weakThis(this);
1176 AutoWeakFrame weakOther(aOther);
1178 if (mInnerView) {
1179 EndSwapDocShellsForViews(mInnerView->GetFirstChild());
1181 if (other->mInnerView) {
1182 EndSwapDocShellsForViews(other->mInnerView->GetFirstChild());
1185 // Now make sure we reflow both frames, in case their contents
1186 // determine their size.
1187 // And repaint them, for good measure, in case there's nothing
1188 // interesting that happens during reflow.
1189 if (weakThis.IsAlive()) {
1190 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors,
1191 NS_FRAME_IS_DIRTY);
1192 InvalidateFrameSubtree();
1193 PropagateIsUnderHiddenEmbedderElement(
1194 PresShell()->IsUnderHiddenEmbedderElement() ||
1195 !StyleVisibility()->IsVisible());
1197 if (weakOther.IsAlive()) {
1198 other->PresShell()->FrameNeedsReflow(
1199 other, IntrinsicDirty::FrameAndAncestors, NS_FRAME_IS_DIRTY);
1200 other->InvalidateFrameSubtree();
1201 other->PropagateIsUnderHiddenEmbedderElement(
1202 other->PresShell()->IsUnderHiddenEmbedderElement() ||
1203 !other->StyleVisibility()->IsVisible());
1207 void nsSubDocumentFrame::ClearDisplayItems() {
1208 if (auto* builder = nsLayoutUtils::GetRetainedDisplayListBuilder(this)) {
1209 DL_LOGD("nsSubDocumentFrame::ClearDisplayItems() %p", this);
1210 builder->ClearRetainedData();
1214 nsView* nsSubDocumentFrame::EnsureInnerView() {
1215 if (mInnerView) {
1216 return mInnerView;
1219 // create, init, set the parent of the view
1220 nsView* outerView = GetView();
1221 NS_ASSERTION(outerView, "Must have an outer view already");
1222 nsRect viewBounds(0, 0, 0, 0); // size will be fixed during reflow
1224 nsViewManager* viewMan = outerView->GetViewManager();
1225 nsView* innerView = viewMan->CreateView(viewBounds, outerView);
1226 if (!innerView) {
1227 NS_ERROR("Could not create inner view");
1228 return nullptr;
1230 mInnerView = innerView;
1231 viewMan->InsertChild(outerView, innerView, nullptr, true);
1233 return mInnerView;
1236 nsPoint nsSubDocumentFrame::GetExtraOffset() const {
1237 MOZ_ASSERT(mInnerView);
1238 return mInnerView->GetPosition();
1241 void nsSubDocumentFrame::SubdocumentIntrinsicSizeOrRatioChanged() {
1242 const nsStylePosition* pos = StylePosition();
1243 bool dependsOnIntrinsics =
1244 !pos->mWidth.ConvertsToLength() || !pos->mHeight.ConvertsToLength();
1246 if (dependsOnIntrinsics || pos->mObjectFit != StyleObjectFit::Fill) {
1247 auto dirtyHint = dependsOnIntrinsics
1248 ? IntrinsicDirty::FrameAncestorsAndDescendants
1249 : IntrinsicDirty::None;
1250 PresShell()->FrameNeedsReflow(this, dirtyHint, NS_FRAME_IS_DIRTY);
1251 InvalidateFrame();
1255 // Return true iff |aManager| is a "temporary layer manager". They're
1256 // used for small software rendering tasks, like drawWindow. That's
1257 // currently implemented by a BasicLayerManager without a backing
1258 // widget, and hence in non-retained mode.
1259 nsDisplayRemote::nsDisplayRemote(nsDisplayListBuilder* aBuilder,
1260 nsSubDocumentFrame* aFrame)
1261 : nsPaintedDisplayItem(aBuilder, aFrame),
1262 mEventRegionsOverride(EventRegionsOverride::NoOverride) {
1263 const bool frameIsPointerEventsNone =
1264 aFrame->Style()->PointerEvents() == StylePointerEvents::None;
1265 if (aBuilder->IsInsidePointerEventsNoneDoc() || frameIsPointerEventsNone) {
1266 mEventRegionsOverride |= EventRegionsOverride::ForceEmptyHitRegion;
1268 if (nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(
1269 aFrame->PresShell())) {
1270 mEventRegionsOverride |= EventRegionsOverride::ForceDispatchToContent;
1273 mPaintData = aFrame->GetRemotePaintData();
1276 namespace mozilla {
1278 void nsDisplayRemote::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
1279 nsPresContext* pc = mFrame->PresContext();
1280 nsFrameLoader* fl = GetFrameLoader();
1281 if (pc->GetPrintSettings() && fl->IsRemoteFrame()) {
1282 // See the comment below in CreateWebRenderCommands() as for why doing this.
1283 fl->UpdatePositionAndSize(static_cast<nsSubDocumentFrame*>(mFrame));
1286 DrawTarget* target = aCtx->GetDrawTarget();
1287 if (!target->IsRecording() || mPaintData.mTabId == 0) {
1288 NS_WARNING("Remote iframe not rendered");
1289 return;
1292 // Rendering the inner document will apply a scale to account for its app
1293 // units per dev pixel ratio. We want to apply the inverse scaling using our
1294 // app units per dev pixel ratio, so that no actual scaling will be applied if
1295 // they match. For in-process rendering, nsSubDocumentFrame creates an
1296 // nsDisplayZoom item if the app units per dev pixel ratio changes.
1298 // Similarly, rendering the inner document will scale up by the cross process
1299 // paint scale again, so we also need to account for that.
1300 const int32_t appUnitsPerDevPixel = pc->AppUnitsPerDevPixel();
1302 gfxContextMatrixAutoSaveRestore saveMatrix(aCtx);
1303 gfxFloat targetAuPerDev =
1304 gfxFloat(AppUnitsPerCSSPixel()) / aCtx->GetCrossProcessPaintScale();
1306 gfxFloat scale = targetAuPerDev / appUnitsPerDevPixel;
1307 aCtx->Multiply(gfxMatrix::Scaling(scale, scale));
1309 Rect destRect =
1310 NSRectToSnappedRect(GetContentRect(), targetAuPerDev, *target);
1311 target->DrawDependentSurface(mPaintData.mTabId, destRect);
1314 bool nsDisplayRemote::CreateWebRenderCommands(
1315 mozilla::wr::DisplayListBuilder& aBuilder,
1316 mozilla::wr::IpcResourceUpdateQueue& aResources,
1317 const StackingContextHelper& aSc,
1318 mozilla::layers::RenderRootStateManager* aManager,
1319 nsDisplayListBuilder* aDisplayListBuilder) {
1320 if (!mPaintData.mLayersId.IsValid()) {
1321 return true;
1324 nsPresContext* pc = mFrame->PresContext();
1325 nsFrameLoader* fl = GetFrameLoader();
1327 auto* subDocFrame = static_cast<nsSubDocumentFrame*>(mFrame);
1328 nsRect destRect = subDocFrame->GetDestRect();
1329 if (RefPtr<RemoteBrowser> remoteBrowser = fl->GetRemoteBrowser()) {
1330 if (pc->GetPrintSettings()) {
1331 // HACK(emilio): Usually we update sizing/positioning from
1332 // ReflowFinished(). Print documents have no incremental reflow at all
1333 // though, so we can't rely on it firing after a frame becomes remote.
1334 // Thus, if we're painting a remote frame, update its sizing and position
1335 // now.
1337 // UpdatePositionAndSize() can cause havoc for non-remote frames but
1338 // luckily we don't care about those, so this is fine.
1339 fl->UpdatePositionAndSize(subDocFrame);
1342 // Adjust mItemVisibleRect, which is relative to the reference frame, to be
1343 // relative to this frame.
1344 const nsRect buildingRect = GetBuildingRect() - ToReferenceFrame();
1345 Maybe<nsRect> visibleRect =
1346 buildingRect.EdgeInclusiveIntersection(destRect);
1347 if (visibleRect) {
1348 *visibleRect -= destRect.TopLeft();
1351 // Generate an effects update notifying the browser it is visible
1352 MatrixScales scale = aSc.GetInheritedScale();
1354 ParentLayerToScreenScale2D transformToAncestorScale =
1355 ParentLayerToParentLayerScale(
1356 pc->GetPresShell() ? pc->GetPresShell()->GetCumulativeResolution()
1357 : 1.f) *
1358 nsLayoutUtils::GetTransformToAncestorScaleCrossProcessForFrameMetrics(
1359 mFrame);
1361 aDisplayListBuilder->AddEffectUpdate(
1362 remoteBrowser, EffectsInfo::VisibleWithinRect(
1363 visibleRect, scale, transformToAncestorScale));
1365 // Create a WebRenderRemoteData to notify the RemoteBrowser when it is no
1366 // longer visible
1367 RefPtr<WebRenderRemoteData> userData =
1368 aManager->CommandBuilder()
1369 .CreateOrRecycleWebRenderUserData<WebRenderRemoteData>(this,
1370 nullptr);
1371 userData->SetRemoteBrowser(remoteBrowser);
1374 nscoord auPerDevPixel = pc->AppUnitsPerDevPixel();
1375 nsPoint layerOffset =
1376 aDisplayListBuilder->ToReferenceFrame(mFrame) + destRect.TopLeft();
1377 mOffset = LayoutDevicePoint::FromAppUnits(layerOffset, auPerDevPixel);
1379 destRect.MoveTo(0, 0);
1380 auto rect = LayoutDeviceRect::FromAppUnits(destRect, auPerDevPixel);
1381 rect += mOffset;
1383 aBuilder.PushIFrame(rect, !BackfaceIsHidden(),
1384 mozilla::wr::AsPipelineId(mPaintData.mLayersId),
1385 /*ignoreMissingPipelines*/ true);
1387 return true;
1390 bool nsDisplayRemote::UpdateScrollData(
1391 mozilla::layers::WebRenderScrollData* aData,
1392 mozilla::layers::WebRenderLayerScrollData* aLayerData) {
1393 if (!mPaintData.mLayersId.IsValid()) {
1394 return true;
1397 if (aLayerData) {
1398 aLayerData->SetReferentId(mPaintData.mLayersId);
1400 auto size = static_cast<nsSubDocumentFrame*>(mFrame)->GetSubdocumentSize();
1401 Matrix4x4 m = Matrix4x4::Translation(mOffset.x, mOffset.y, 0.0);
1402 aLayerData->SetTransform(m);
1403 aLayerData->SetEventRegionsOverride(mEventRegionsOverride);
1404 aLayerData->SetRemoteDocumentSize(LayerIntSize(size.width, size.height));
1406 return true;
1409 nsFrameLoader* nsDisplayRemote::GetFrameLoader() const {
1410 return static_cast<nsSubDocumentFrame*>(mFrame)->FrameLoader();
1413 } // namespace mozilla