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