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