Bumping manifests a=b2g-bump
[gecko.git] / layout / generic / nsSubDocumentFrame.cpp
blob1039bfd5a7d218705d4677826d547db73ab88476
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /*
7 * rendering object for replaced elements that contain a document, such
8 * as <frame>, <iframe>, and some <object>s
9 */
11 #include "nsSubDocumentFrame.h"
13 #include "mozilla/layout/RenderFrameParent.h"
15 #include "nsCOMPtr.h"
16 #include "nsGenericHTMLElement.h"
17 #include "nsGenericHTMLFrameElement.h"
18 #include "nsAttrValueInlines.h"
19 #include "nsIDocShell.h"
20 #include "nsIContentViewer.h"
21 #include "nsPresContext.h"
22 #include "nsIPresShell.h"
23 #include "nsIDocument.h"
24 #include "nsView.h"
25 #include "nsViewManager.h"
26 #include "nsGkAtoms.h"
27 #include "nsStyleConsts.h"
28 #include "nsFrameSetFrame.h"
29 #include "nsIDOMHTMLFrameElement.h"
30 #include "nsIScrollable.h"
31 #include "nsNameSpaceManager.h"
32 #include "nsDisplayList.h"
33 #include "nsIScrollableFrame.h"
34 #include "nsIObjectLoadingContent.h"
35 #include "nsLayoutUtils.h"
36 #include "FrameLayerBuilder.h"
37 #include "nsObjectFrame.h"
38 #include "nsContentUtils.h"
39 #include "nsIPermissionManager.h"
40 #include "nsServiceManagerUtils.h"
42 using namespace mozilla;
43 using mozilla::layout::RenderFrameParent;
45 static nsIDocument*
46 GetDocumentFromView(nsView* aView)
48 NS_PRECONDITION(aView, "");
50 nsIFrame* f = aView->GetFrame();
51 nsIPresShell* ps = f ? f->PresContext()->PresShell() : nullptr;
52 return ps ? ps->GetDocument() : nullptr;
55 nsSubDocumentFrame::nsSubDocumentFrame(nsStyleContext* aContext)
56 : nsLeafFrame(aContext)
57 , mIsInline(false)
58 , mPostedReflowCallback(false)
59 , mDidCreateDoc(false)
60 , mCallingShow(false)
64 #ifdef ACCESSIBILITY
65 a11y::AccType
66 nsSubDocumentFrame::AccessibleType()
68 return a11y::eOuterDocType;
70 #endif
72 NS_QUERYFRAME_HEAD(nsSubDocumentFrame)
73 NS_QUERYFRAME_ENTRY(nsSubDocumentFrame)
74 NS_QUERYFRAME_TAIL_INHERITING(nsLeafFrame)
76 class AsyncFrameInit : public nsRunnable
78 public:
79 explicit AsyncFrameInit(nsIFrame* aFrame) : mFrame(aFrame) {}
80 NS_IMETHOD Run()
82 if (mFrame.IsAlive()) {
83 static_cast<nsSubDocumentFrame*>(mFrame.GetFrame())->ShowViewer();
85 return NS_OK;
87 private:
88 nsWeakFrame mFrame;
91 static void
92 InsertViewsInReverseOrder(nsView* aSibling, nsView* aParent);
94 static void
95 EndSwapDocShellsForViews(nsView* aView);
97 void
98 nsSubDocumentFrame::Init(nsIContent* aContent,
99 nsContainerFrame* aParent,
100 nsIFrame* aPrevInFlow)
102 // determine if we are a <frame> or <iframe>
103 if (aContent) {
104 nsCOMPtr<nsIDOMHTMLFrameElement> frameElem = do_QueryInterface(aContent);
105 mIsInline = frameElem ? false : true;
108 nsLeafFrame::Init(aContent, aParent, aPrevInFlow);
110 // We are going to create an inner view. If we need a view for the
111 // OuterFrame but we wait for the normal view creation path in
112 // nsCSSFrameConstructor, then we will lose because the inner view's
113 // parent will already have been set to some outer view (e.g., the
114 // canvas) when it really needs to have this frame's view as its
115 // parent. So, create this frame's view right away, whether we
116 // really need it or not, and the inner view will get it as the
117 // parent.
118 if (!HasView()) {
119 nsContainerFrame::CreateViewForFrame(this, true);
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 nsRefPtr<nsFrameLoader> frameloader = FrameLoader();
132 if (frameloader) {
133 nsCOMPtr<nsIDocument> oldContainerDoc;
134 nsView* detachedViews =
135 frameloader->GetDetachedSubdocView(getter_AddRefs(oldContainerDoc));
136 if (detachedViews) {
137 if (oldContainerDoc == aContent->OwnerDoc()) {
138 // Restore stashed presentation.
139 ::InsertViewsInReverseOrder(detachedViews, mInnerView);
140 ::EndSwapDocShellsForViews(mInnerView->GetFirstChild());
141 } else {
142 // Presentation is for a different document, don't restore it.
143 frameloader->Hide();
146 frameloader->SetDetachedSubdocView(nullptr, nullptr);
149 nsContentUtils::AddScriptRunner(new AsyncFrameInit(this));
152 void
153 nsSubDocumentFrame::ShowViewer()
155 if (mCallingShow) {
156 return;
159 if (!PresContext()->IsDynamic()) {
160 // We let the printing code take care of loading the document; just
161 // create the inner view for it to use.
162 (void) EnsureInnerView();
163 } else {
164 nsRefPtr<nsFrameLoader> frameloader = FrameLoader();
165 if (frameloader) {
166 nsIntSize margin = GetMarginAttributes();
167 nsWeakFrame weakThis(this);
168 mCallingShow = true;
169 const nsAttrValue* attrValue =
170 GetContent()->AsElement()->GetParsedAttr(nsGkAtoms::scrolling);
171 int32_t scrolling =
172 nsGenericHTMLFrameElement::MapScrollingAttribute(attrValue);
173 bool didCreateDoc =
174 frameloader->Show(margin.width, margin.height,
175 scrolling, scrolling, this);
176 if (!weakThis.IsAlive()) {
177 return;
179 mCallingShow = false;
180 mDidCreateDoc = didCreateDoc;
185 nsIFrame*
186 nsSubDocumentFrame::GetSubdocumentRootFrame()
188 if (!mInnerView)
189 return nullptr;
190 nsView* subdocView = mInnerView->GetFirstChild();
191 return subdocView ? subdocView->GetFrame() : nullptr;
194 nsIPresShell*
195 nsSubDocumentFrame::GetSubdocumentPresShellForPainting(uint32_t aFlags)
197 if (!mInnerView)
198 return nullptr;
200 nsView* subdocView = mInnerView->GetFirstChild();
201 if (!subdocView)
202 return nullptr;
204 nsIPresShell* presShell = nullptr;
206 nsIFrame* subdocRootFrame = subdocView->GetFrame();
207 if (subdocRootFrame) {
208 presShell = subdocRootFrame->PresContext()->PresShell();
211 // If painting is suppressed in the presshell, we try to look for a better
212 // presshell to use.
213 if (!presShell || (presShell->IsPaintingSuppressed() &&
214 !(aFlags & IGNORE_PAINT_SUPPRESSION))) {
215 // During page transition mInnerView will sometimes have two children, the
216 // first being the new page that may not have any frame, and the second
217 // being the old page that will probably have a frame.
218 nsView* nextView = subdocView->GetNextSibling();
219 nsIFrame* frame = nullptr;
220 if (nextView) {
221 frame = nextView->GetFrame();
223 if (frame) {
224 nsIPresShell* ps = frame->PresContext()->PresShell();
225 if (!presShell || (ps && !ps->IsPaintingSuppressed())) {
226 subdocView = nextView;
227 subdocRootFrame = frame;
228 presShell = ps;
231 if (!presShell) {
232 // If we don't have a frame we use this roundabout way to get the pres shell.
233 if (!mFrameLoader)
234 return nullptr;
235 nsCOMPtr<nsIDocShell> docShell;
236 mFrameLoader->GetDocShell(getter_AddRefs(docShell));
237 if (!docShell)
238 return nullptr;
239 presShell = docShell->GetPresShell();
243 return presShell;
249 nsIntSize
250 nsSubDocumentFrame::GetSubdocumentSize()
252 if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
253 nsRefPtr<nsFrameLoader> frameloader = FrameLoader();
254 if (frameloader) {
255 nsCOMPtr<nsIDocument> oldContainerDoc;
256 nsView* detachedViews =
257 frameloader->GetDetachedSubdocView(getter_AddRefs(oldContainerDoc));
258 if (detachedViews) {
259 nsSize size = detachedViews->GetBounds().Size();
260 nsPresContext* presContext = detachedViews->GetFrame()->PresContext();
261 return nsIntSize(presContext->AppUnitsToDevPixels(size.width),
262 presContext->AppUnitsToDevPixels(size.height));
265 // Pick some default size for now. Using 10x10 because that's what the
266 // code used to do.
267 return nsIntSize(10, 10);
268 } else {
269 nsSize docSizeAppUnits;
270 nsPresContext* presContext = PresContext();
271 nsCOMPtr<nsIDOMHTMLFrameElement> frameElem =
272 do_QueryInterface(GetContent());
273 if (frameElem) {
274 docSizeAppUnits = GetSize();
275 } else {
276 docSizeAppUnits = GetContentRect().Size();
278 return nsIntSize(presContext->AppUnitsToDevPixels(docSizeAppUnits.width),
279 presContext->AppUnitsToDevPixels(docSizeAppUnits.height));
283 bool
284 nsSubDocumentFrame::PassPointerEventsToChildren()
286 // Limit use of mozpasspointerevents to documents with embedded:apps/chrome
287 // permission, because this could be used by the parent document to discover
288 // which parts of the subdocument are transparent to events (if subdocument
289 // uses pointer-events:none on its root element, which is admittedly
290 // unlikely)
291 if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozpasspointerevents)) {
292 if (PresContext()->IsChrome()) {
293 return true;
296 nsCOMPtr<nsIPermissionManager> permMgr =
297 services::GetPermissionManager();
298 if (permMgr) {
299 uint32_t permission = nsIPermissionManager::DENY_ACTION;
300 permMgr->TestPermissionFromPrincipal(GetContent()->NodePrincipal(),
301 "embed-apps", &permission);
303 return permission == nsIPermissionManager::ALLOW_ACTION;
307 return false;
310 static void
311 WrapBackgroundColorInOwnLayer(nsDisplayListBuilder* aBuilder,
312 nsIFrame* aFrame,
313 nsDisplayList* aList)
315 nsDisplayList tempItems;
316 nsDisplayItem* item;
317 while ((item = aList->RemoveBottom()) != nullptr) {
318 if (item->GetType() == nsDisplayItem::TYPE_BACKGROUND_COLOR) {
319 nsDisplayList tmpList;
320 tmpList.AppendToTop(item);
321 item = new (aBuilder) nsDisplayOwnLayer(aBuilder, aFrame, &tmpList);
323 tempItems.AppendToTop(item);
325 aList->AppendToTop(&tempItems);
328 void
329 nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
330 const nsRect& aDirtyRect,
331 const nsDisplayListSet& aLists)
333 if (!IsVisibleForPainting(aBuilder))
334 return;
336 nsFrameLoader* frameLoader = FrameLoader();
337 RenderFrameParent* rfp = nullptr;
338 if (frameLoader) {
339 rfp = frameLoader->GetCurrentRemoteFrame();
342 // If we are pointer-events:none then we don't need to HitTest background
343 bool pointerEventsNone = StyleVisibility()->mPointerEvents == NS_STYLE_POINTER_EVENTS_NONE;
344 if (!aBuilder->IsForEventDelivery() || !pointerEventsNone) {
345 nsDisplayListCollection decorations;
346 DisplayBorderBackgroundOutline(aBuilder, decorations);
347 if (rfp) {
348 // Wrap background colors of <iframe>s with remote subdocuments in their
349 // own layer so we generate a ColorLayer. This is helpful for optimizing
350 // compositing; we can skip compositing the ColorLayer when the
351 // remote content is opaque.
352 WrapBackgroundColorInOwnLayer(aBuilder, this, decorations.BorderBackground());
354 decorations.MoveTo(aLists);
357 bool passPointerEventsToChildren = false;
358 if (aBuilder->IsForEventDelivery()) {
359 passPointerEventsToChildren = PassPointerEventsToChildren();
360 // If mozpasspointerevents is set, then we should allow subdocument content
361 // to handle events even if we're pointer-events:none.
362 if (pointerEventsNone && !passPointerEventsToChildren) {
363 return;
367 // If we're passing pointer events to children then we have to descend into
368 // subdocuments no matter what, to determine which parts are transparent for
369 // elementFromPoint.
370 if (!mInnerView ||
371 (!aBuilder->GetDescendIntoSubdocuments() && !passPointerEventsToChildren)) {
372 return;
375 if (rfp) {
376 rfp->BuildDisplayList(aBuilder, this, aDirtyRect, aLists);
377 return;
380 nsCOMPtr<nsIPresShell> presShell =
381 GetSubdocumentPresShellForPainting(
382 aBuilder->IsIgnoringPaintSuppression() ? IGNORE_PAINT_SUPPRESSION : 0);
384 if (!presShell) {
385 return;
388 nsIFrame* subdocRootFrame = presShell->GetRootFrame();
390 nsPresContext* presContext = presShell->GetPresContext();
392 int32_t parentAPD = PresContext()->AppUnitsPerDevPixel();
393 int32_t subdocAPD = presContext->AppUnitsPerDevPixel();
395 nsRect dirty;
396 bool haveDisplayPort = false;
397 bool ignoreViewportScrolling = false;
398 nsIFrame* savedIgnoreScrollFrame = nullptr;
399 if (subdocRootFrame) {
400 // get the dirty rect relative to the root frame of the subdoc
401 dirty = aDirtyRect + GetOffsetToCrossDoc(subdocRootFrame);
402 // and convert into the appunits of the subdoc
403 dirty = dirty.ConvertAppUnitsRoundOut(parentAPD, subdocAPD);
405 if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
406 // for root content documents we want the base to be the composition bounds
407 nsRect displayportBase = presContext->IsRootContentDocument() ?
408 nsRect(nsPoint(0,0), nsLayoutUtils::CalculateCompositionSizeForFrame(rootScrollFrame)) :
409 dirty.Intersect(nsRect(nsPoint(0,0), subdocRootFrame->GetSize()));
410 nsRect displayPort;
411 if (aBuilder->IsPaintingToWindow() &&
412 nsLayoutUtils::GetOrMaybeCreateDisplayPort(
413 *aBuilder, rootScrollFrame, displayportBase, &displayPort)) {
414 haveDisplayPort = true;
415 dirty = displayPort;
418 ignoreViewportScrolling = presShell->IgnoringViewportScrolling();
419 if (ignoreViewportScrolling) {
420 savedIgnoreScrollFrame = aBuilder->GetIgnoreScrollFrame();
421 aBuilder->SetIgnoreScrollFrame(rootScrollFrame);
423 if (aBuilder->IsForImageVisibility()) {
424 // The ExpandRectToNearlyVisible that the root scroll frame would do gets short
425 // circuited due to us ignoring the root scroll frame, so we do it here.
426 nsIScrollableFrame* rootScrollableFrame = do_QueryFrame(rootScrollFrame);
427 dirty = rootScrollableFrame->ExpandRectToNearlyVisible(dirty);
432 aBuilder->EnterPresShell(subdocRootFrame, dirty);
433 } else {
434 dirty = aDirtyRect;
437 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
438 if (ShouldClipSubdocument()) {
439 clipState.ClipContainingBlockDescendantsToContentBox(aBuilder, this);
442 nsIScrollableFrame *sf = presShell->GetRootScrollFrameAsScrollable();
443 bool constructResolutionItem = subdocRootFrame &&
444 (presShell->GetXResolution() != 1.0 || presShell->GetYResolution() != 1.0);
445 bool constructZoomItem = subdocRootFrame && parentAPD != subdocAPD;
446 bool needsOwnLayer = constructResolutionItem || constructZoomItem ||
447 haveDisplayPort ||
448 presContext->IsRootContentDocument() || (sf && sf->IsScrollingActive());
450 nsDisplayList childItems;
453 DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
454 if (needsOwnLayer) {
455 // Clear current clip. There's no point in propagating it down, since
456 // the layer we will construct will be clipped by the current clip.
457 // In fact for nsDisplayZoom propagating it down would be incorrect since
458 // nsDisplayZoom changes the meaning of appunits.
459 nestedClipState.Clear();
462 if (subdocRootFrame) {
463 nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
464 nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(
465 aBuilder,
466 ignoreViewportScrolling && rootScrollFrame && rootScrollFrame->GetContent()
467 ? nsLayoutUtils::FindOrCreateIDFor(rootScrollFrame->GetContent())
468 : aBuilder->GetCurrentScrollParentId());
470 aBuilder->SetAncestorHasTouchEventHandler(false);
471 subdocRootFrame->
472 BuildDisplayListForStackingContext(aBuilder, dirty, &childItems);
475 if (!aBuilder->IsForEventDelivery()) {
476 // If we are going to use a displayzoom below then any items we put under
477 // it need to have underlying frames from the subdocument. So we need to
478 // calculate the bounds based on which frame will be the underlying frame
479 // for the canvas background color item.
480 nsRect bounds = GetContentRectRelativeToSelf() +
481 aBuilder->ToReferenceFrame(this);
482 if (subdocRootFrame) {
483 bounds = bounds.ConvertAppUnitsRoundOut(parentAPD, subdocAPD);
486 // If we are in print preview/page layout we want to paint the grey
487 // background behind the page, not the canvas color. The canvas color gets
488 // painted on the page itself.
489 if (nsLayoutUtils::NeedsPrintPreviewBackground(presContext)) {
490 presShell->AddPrintPreviewBackgroundItem(
491 *aBuilder, childItems, subdocRootFrame ? subdocRootFrame : this,
492 bounds);
493 } else {
494 // Invoke AutoBuildingDisplayList to ensure that the correct dirty rect
495 // is used to compute the visible rect if AddCanvasBackgroundColorItem
496 // creates a display item.
497 nsIFrame* frame = subdocRootFrame ? subdocRootFrame : this;
498 nsDisplayListBuilder::AutoBuildingDisplayList
499 building(aBuilder, frame, dirty, true);
500 // Add the canvas background color to the bottom of the list. This
501 // happens after we've built the list so that AddCanvasBackgroundColorItem
502 // can monkey with the contents if necessary.
503 uint32_t flags = nsIPresShell::FORCE_DRAW;
504 presShell->AddCanvasBackgroundColorItem(
505 *aBuilder, childItems, frame, bounds, NS_RGBA(0,0,0,0), flags);
510 if (subdocRootFrame) {
511 aBuilder->LeavePresShell(subdocRootFrame, dirty);
513 if (ignoreViewportScrolling) {
514 aBuilder->SetIgnoreScrollFrame(savedIgnoreScrollFrame);
518 // Generate a resolution and/or zoom item if needed. If one or both of those is
519 // created, we don't need to create a separate nsDisplaySubDocument.
521 uint32_t flags = nsDisplayOwnLayer::GENERATE_SUBDOC_INVALIDATIONS;
522 // If ignoreViewportScrolling is true then the top most layer we create here
523 // is going to become the scrollable layer for the root scroll frame, so we
524 // want to add nsDisplayOwnLayer::GENERATE_SCROLLABLE_LAYER to whatever layer
525 // becomes the topmost. We do this below.
526 if (constructZoomItem) {
527 uint32_t zoomFlags = flags;
528 if (ignoreViewportScrolling && !constructResolutionItem) {
529 zoomFlags |= nsDisplayOwnLayer::GENERATE_SCROLLABLE_LAYER;
531 nsDisplayZoom* zoomItem =
532 new (aBuilder) nsDisplayZoom(aBuilder, subdocRootFrame, &childItems,
533 subdocAPD, parentAPD, zoomFlags);
534 childItems.AppendToTop(zoomItem);
535 needsOwnLayer = false;
537 // Wrap the zoom item in the resolution item if we have both because we want the
538 // resolution scale applied on top of the app units per dev pixel conversion.
539 if (ignoreViewportScrolling) {
540 flags |= nsDisplayOwnLayer::GENERATE_SCROLLABLE_LAYER;
542 if (constructResolutionItem) {
543 nsDisplayResolution* resolutionItem =
544 new (aBuilder) nsDisplayResolution(aBuilder, subdocRootFrame, &childItems,
545 flags);
546 childItems.AppendToTop(resolutionItem);
547 needsOwnLayer = false;
549 if (needsOwnLayer) {
550 // We always want top level content documents to be in their own layer.
551 nsDisplaySubDocument* layerItem = new (aBuilder) nsDisplaySubDocument(
552 aBuilder, subdocRootFrame ? subdocRootFrame : this,
553 &childItems, flags);
554 childItems.AppendToTop(layerItem);
557 if (aBuilder->IsForImageVisibility()) {
558 // We don't add the childItems to the return list as we're dealing with them here.
559 presShell->RebuildImageVisibilityDisplayList(childItems);
560 childItems.DeleteAll();
561 } else {
562 aLists.Content()->AppendToTop(&childItems);
566 nscoord
567 nsSubDocumentFrame::GetIntrinsicISize()
569 if (!IsInline()) {
570 return 0; // HTML <frame> has no useful intrinsic width
573 if (mContent->IsXUL()) {
574 return 0; // XUL <iframe> and <browser> have no useful intrinsic width
577 NS_ASSERTION(ObtainIntrinsicSizeFrame() == nullptr,
578 "Intrinsic width should come from the embedded document.");
580 // We must be an HTML <iframe>. Default to a width of 300, for IE
581 // compat (and per CSS2.1 draft).
582 return nsPresContext::CSSPixelsToAppUnits(300);
585 nscoord
586 nsSubDocumentFrame::GetIntrinsicBSize()
588 // <frame> processing does not use this routine, only <iframe>
589 NS_ASSERTION(IsInline(), "Shouldn't have been called");
591 if (mContent->IsXUL()) {
592 return 0;
595 NS_ASSERTION(ObtainIntrinsicSizeFrame() == nullptr,
596 "Intrinsic height should come from the embedded document.");
598 // Use 150px, for compatibility with IE, and per CSS2.1 draft.
599 return nsPresContext::CSSPixelsToAppUnits(150);
602 #ifdef DEBUG_FRAME_DUMP
603 void
604 nsSubDocumentFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
606 nsCString str;
607 ListGeneric(str, aPrefix, aFlags);
608 fprintf_stderr(out, "%s\n", str.get());
610 if (aFlags & TRAVERSE_SUBDOCUMENT_FRAMES) {
611 nsSubDocumentFrame* f = const_cast<nsSubDocumentFrame*>(this);
612 nsIFrame* subdocRootFrame = f->GetSubdocumentRootFrame();
613 if (subdocRootFrame) {
614 nsCString pfx(aPrefix);
615 pfx += " ";
616 subdocRootFrame->List(out, pfx.get(), aFlags);
621 nsresult nsSubDocumentFrame::GetFrameName(nsAString& aResult) const
623 return MakeFrameName(NS_LITERAL_STRING("FrameOuter"), aResult);
625 #endif
627 nsIAtom*
628 nsSubDocumentFrame::GetType() const
630 return nsGkAtoms::subDocumentFrame;
633 /* virtual */ nscoord
634 nsSubDocumentFrame::GetMinISize(nsRenderingContext *aRenderingContext)
636 nscoord result;
637 DISPLAY_MIN_WIDTH(this, result);
639 nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
640 if (subDocRoot) {
641 result = subDocRoot->GetMinISize(aRenderingContext);
642 } else {
643 result = GetIntrinsicISize();
646 return result;
649 /* virtual */ nscoord
650 nsSubDocumentFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
652 nscoord result;
653 DISPLAY_PREF_WIDTH(this, result);
655 nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
656 if (subDocRoot) {
657 result = subDocRoot->GetPrefISize(aRenderingContext);
658 } else {
659 result = GetIntrinsicISize();
662 return result;
665 /* virtual */ IntrinsicSize
666 nsSubDocumentFrame::GetIntrinsicSize()
668 nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
669 if (subDocRoot) {
670 return subDocRoot->GetIntrinsicSize();
672 return nsLeafFrame::GetIntrinsicSize();
675 /* virtual */ nsSize
676 nsSubDocumentFrame::GetIntrinsicRatio()
678 nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
679 if (subDocRoot) {
680 return subDocRoot->GetIntrinsicRatio();
682 return nsLeafFrame::GetIntrinsicRatio();
685 /* virtual */
686 LogicalSize
687 nsSubDocumentFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
688 WritingMode aWM,
689 const LogicalSize& aCBSize,
690 nscoord aAvailableISize,
691 const LogicalSize& aMargin,
692 const LogicalSize& aBorder,
693 const LogicalSize& aPadding,
694 bool aShrinkWrap)
696 if (!IsInline()) {
697 return nsFrame::ComputeAutoSize(aRenderingContext, aWM, aCBSize,
698 aAvailableISize, aMargin, aBorder,
699 aPadding, aShrinkWrap);
702 return nsLeafFrame::ComputeAutoSize(aRenderingContext, aWM, aCBSize,
703 aAvailableISize, aMargin, aBorder,
704 aPadding, aShrinkWrap);
708 /* virtual */
709 LogicalSize
710 nsSubDocumentFrame::ComputeSize(nsRenderingContext *aRenderingContext,
711 WritingMode aWM,
712 const LogicalSize& aCBSize,
713 nscoord aAvailableISize,
714 const LogicalSize& aMargin,
715 const LogicalSize& aBorder,
716 const LogicalSize& aPadding,
717 uint32_t aFlags)
719 nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
720 if (subDocRoot) {
721 return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(aWM,
722 aRenderingContext, this,
723 subDocRoot->GetIntrinsicSize(),
724 subDocRoot->GetIntrinsicRatio(),
725 aCBSize,
726 aMargin,
727 aBorder,
728 aPadding);
730 return nsLeafFrame::ComputeSize(aRenderingContext, aWM,
731 aCBSize, aAvailableISize,
732 aMargin, aBorder, aPadding, aFlags);
735 void
736 nsSubDocumentFrame::Reflow(nsPresContext* aPresContext,
737 nsHTMLReflowMetrics& aDesiredSize,
738 const nsHTMLReflowState& aReflowState,
739 nsReflowStatus& aStatus)
741 DO_GLOBAL_REFLOW_COUNT("nsSubDocumentFrame");
742 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
743 // printf("OuterFrame::Reflow %X (%d,%d) \n", this, aReflowState.AvailableWidth(), aReflowState.AvailableHeight());
744 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
745 ("enter nsSubDocumentFrame::Reflow: maxSize=%d,%d",
746 aReflowState.AvailableWidth(), aReflowState.AvailableHeight()));
748 aStatus = NS_FRAME_COMPLETE;
750 NS_ASSERTION(mContent->GetPrimaryFrame() == this,
751 "Shouldn't happen");
753 // XUL <iframe> or <browser>, or HTML <iframe>, <object> or <embed>
754 nsLeafFrame::DoReflow(aPresContext, aDesiredSize, aReflowState, aStatus);
756 // "offset" is the offset of our content area from our frame's
757 // top-left corner.
758 nsPoint offset = nsPoint(aReflowState.ComputedPhysicalBorderPadding().left,
759 aReflowState.ComputedPhysicalBorderPadding().top);
761 nsSize innerSize(aDesiredSize.Width(), aDesiredSize.Height());
762 innerSize.width -= aReflowState.ComputedPhysicalBorderPadding().LeftRight();
763 innerSize.height -= aReflowState.ComputedPhysicalBorderPadding().TopBottom();
765 if (mInnerView) {
766 nsViewManager* vm = mInnerView->GetViewManager();
767 vm->MoveViewTo(mInnerView, offset.x, offset.y);
768 vm->ResizeView(mInnerView, nsRect(nsPoint(0, 0), innerSize), true);
771 aDesiredSize.SetOverflowAreasToDesiredBounds();
772 if (!ShouldClipSubdocument()) {
773 nsIFrame* subdocRootFrame = GetSubdocumentRootFrame();
774 if (subdocRootFrame) {
775 aDesiredSize.mOverflowAreas.UnionWith(subdocRootFrame->GetOverflowAreas() + offset);
779 FinishAndStoreOverflow(&aDesiredSize);
781 if (!aPresContext->IsPaginated() && !mPostedReflowCallback) {
782 PresContext()->PresShell()->PostReflowCallback(this);
783 mPostedReflowCallback = true;
786 // printf("OuterFrame::Reflow DONE %X (%d,%d)\n", this,
787 // aDesiredSize.Width(), aDesiredSize.Height());
789 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
790 ("exit nsSubDocumentFrame::Reflow: size=%d,%d status=%x",
791 aDesiredSize.Width(), aDesiredSize.Height(), aStatus));
793 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
796 bool
797 nsSubDocumentFrame::ReflowFinished()
799 if (mFrameLoader) {
800 nsWeakFrame weakFrame(this);
802 mFrameLoader->UpdatePositionAndSize(this);
804 if (weakFrame.IsAlive()) {
805 // Make sure that we can post a reflow callback in the future.
806 mPostedReflowCallback = false;
808 } else {
809 mPostedReflowCallback = false;
811 return false;
814 void
815 nsSubDocumentFrame::ReflowCallbackCanceled()
817 mPostedReflowCallback = false;
820 nsresult
821 nsSubDocumentFrame::AttributeChanged(int32_t aNameSpaceID,
822 nsIAtom* aAttribute,
823 int32_t aModType)
825 if (aNameSpaceID != kNameSpaceID_None) {
826 return NS_OK;
829 // If the noResize attribute changes, dis/allow frame to be resized
830 if (aAttribute == nsGkAtoms::noresize) {
831 // Note that we're not doing content type checks, but that's ok -- if
832 // they'd fail we will just end up with a null framesetFrame.
833 if (mContent->GetParent()->Tag() == nsGkAtoms::frameset) {
834 nsIFrame* parentFrame = GetParent();
836 if (parentFrame) {
837 // There is no interface for nsHTMLFramesetFrame so QI'ing to
838 // concrete class, yay!
839 nsHTMLFramesetFrame* framesetFrame = do_QueryFrame(parentFrame);
840 if (framesetFrame) {
841 framesetFrame->RecalculateBorderResize();
846 else if (aAttribute == nsGkAtoms::showresizer) {
847 nsIFrame* rootFrame = GetSubdocumentRootFrame();
848 if (rootFrame) {
849 rootFrame->PresContext()->PresShell()->
850 FrameNeedsReflow(rootFrame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
853 else if (aAttribute == nsGkAtoms::marginwidth ||
854 aAttribute == nsGkAtoms::marginheight) {
856 // Retrieve the attributes
857 nsIntSize margins = GetMarginAttributes();
859 // Notify the frameloader
860 nsRefPtr<nsFrameLoader> frameloader = FrameLoader();
861 if (frameloader)
862 frameloader->MarginsChanged(margins.width, margins.height);
865 return NS_OK;
868 nsIFrame*
869 NS_NewSubDocumentFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
871 return new (aPresShell) nsSubDocumentFrame(aContext);
874 NS_IMPL_FRAMEARENA_HELPERS(nsSubDocumentFrame)
876 class nsHideViewer : public nsRunnable {
877 public:
878 nsHideViewer(nsIContent* aFrameElement,
879 nsFrameLoader* aFrameLoader,
880 nsIPresShell* aPresShell,
881 bool aHideViewerIfFrameless)
882 : mFrameElement(aFrameElement),
883 mFrameLoader(aFrameLoader),
884 mPresShell(aPresShell),
885 mHideViewerIfFrameless(aHideViewerIfFrameless)
887 NS_ASSERTION(mFrameElement, "Must have a frame element");
888 NS_ASSERTION(mFrameLoader, "Must have a frame loader");
889 NS_ASSERTION(mPresShell, "Must have a presshell");
892 NS_IMETHOD Run()
894 // Flush frames, to ensure any pending display:none changes are made.
895 // Note it can be unsafe to flush if we've destroyed the presentation
896 // for some other reason, like if we're shutting down.
897 if (!mPresShell->IsDestroying()) {
898 mPresShell->FlushPendingNotifications(Flush_Frames);
900 nsIFrame* frame = mFrameElement->GetPrimaryFrame();
901 if ((!frame && mHideViewerIfFrameless) ||
902 mPresShell->IsDestroying()) {
903 // Either the frame element has no nsIFrame or the presshell is being
904 // destroyed. Hide the nsFrameLoader, which destroys the presentation,
905 // and clear our references to the stashed presentation.
906 mFrameLoader->SetDetachedSubdocView(nullptr, nullptr);
907 mFrameLoader->Hide();
909 return NS_OK;
911 private:
912 nsCOMPtr<nsIContent> mFrameElement;
913 nsRefPtr<nsFrameLoader> mFrameLoader;
914 nsCOMPtr<nsIPresShell> mPresShell;
915 bool mHideViewerIfFrameless;
918 static nsView*
919 BeginSwapDocShellsForViews(nsView* aSibling);
921 void
922 nsSubDocumentFrame::DestroyFrom(nsIFrame* aDestructRoot)
924 if (mPostedReflowCallback) {
925 PresContext()->PresShell()->CancelReflowCallback(this);
926 mPostedReflowCallback = false;
929 // Detach the subdocument's views and stash them in the frame loader.
930 // We can then reattach them if we're being reframed (for example if
931 // the frame has been made position:fixed).
932 nsFrameLoader* frameloader = FrameLoader();
933 if (frameloader) {
934 nsView* detachedViews = ::BeginSwapDocShellsForViews(mInnerView->GetFirstChild());
935 frameloader->SetDetachedSubdocView(detachedViews, mContent->OwnerDoc());
937 // We call nsFrameLoader::HideViewer() in a script runner so that we can
938 // safely determine whether the frame is being reframed or destroyed.
939 nsContentUtils::AddScriptRunner(
940 new nsHideViewer(mContent,
941 mFrameLoader,
942 PresContext()->PresShell(),
943 (mDidCreateDoc || mCallingShow)));
946 nsLeafFrame::DestroyFrom(aDestructRoot);
949 nsIntSize
950 nsSubDocumentFrame::GetMarginAttributes()
952 nsIntSize result(-1, -1);
953 nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent);
954 if (content) {
955 const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::marginwidth);
956 if (attr && attr->Type() == nsAttrValue::eInteger)
957 result.width = attr->GetIntegerValue();
958 attr = content->GetParsedAttr(nsGkAtoms::marginheight);
959 if (attr && attr->Type() == nsAttrValue::eInteger)
960 result.height = attr->GetIntegerValue();
962 return result;
965 nsFrameLoader*
966 nsSubDocumentFrame::FrameLoader()
968 nsIContent* content = GetContent();
969 if (!content)
970 return nullptr;
972 if (!mFrameLoader) {
973 nsCOMPtr<nsIFrameLoaderOwner> loaderOwner = do_QueryInterface(content);
974 if (loaderOwner) {
975 nsCOMPtr<nsIFrameLoader> loader;
976 loaderOwner->GetFrameLoader(getter_AddRefs(loader));
977 mFrameLoader = static_cast<nsFrameLoader*>(loader.get());
980 return mFrameLoader;
983 // XXX this should be called ObtainDocShell or something like that,
984 // to indicate that it could have side effects
985 nsresult
986 nsSubDocumentFrame::GetDocShell(nsIDocShell **aDocShell)
988 *aDocShell = nullptr;
990 NS_ENSURE_STATE(FrameLoader());
991 return mFrameLoader->GetDocShell(aDocShell);
994 static void
995 DestroyDisplayItemDataForFrames(nsIFrame* aFrame)
997 FrameLayerBuilder::DestroyDisplayItemDataFor(aFrame);
999 nsIFrame::ChildListIterator lists(aFrame);
1000 for (; !lists.IsDone(); lists.Next()) {
1001 nsFrameList::Enumerator childFrames(lists.CurrentList());
1002 for (; !childFrames.AtEnd(); childFrames.Next()) {
1003 DestroyDisplayItemDataForFrames(childFrames.get());
1008 static bool
1009 BeginSwapDocShellsForDocument(nsIDocument* aDocument, void*)
1011 NS_PRECONDITION(aDocument, "");
1013 nsIPresShell* shell = aDocument->GetShell();
1014 if (shell) {
1015 // Disable painting while the views are detached, see bug 946929.
1016 shell->SetNeverPainting(true);
1018 nsIFrame* rootFrame = shell->GetRootFrame();
1019 if (rootFrame) {
1020 ::DestroyDisplayItemDataForFrames(rootFrame);
1023 aDocument->EnumerateActivityObservers(
1024 nsObjectFrame::BeginSwapDocShells, nullptr);
1025 aDocument->EnumerateSubDocuments(BeginSwapDocShellsForDocument, nullptr);
1026 return true;
1029 static nsView*
1030 BeginSwapDocShellsForViews(nsView* aSibling)
1032 // Collect the removed sibling views in reverse order in 'removedViews'.
1033 nsView* removedViews = nullptr;
1034 while (aSibling) {
1035 nsIDocument* doc = ::GetDocumentFromView(aSibling);
1036 if (doc) {
1037 ::BeginSwapDocShellsForDocument(doc, nullptr);
1039 nsView* next = aSibling->GetNextSibling();
1040 aSibling->GetViewManager()->RemoveChild(aSibling);
1041 aSibling->SetNextSibling(removedViews);
1042 removedViews = aSibling;
1043 aSibling = next;
1045 return removedViews;
1048 static void
1049 InsertViewsInReverseOrder(nsView* aSibling, nsView* aParent)
1051 NS_PRECONDITION(aParent, "");
1052 NS_PRECONDITION(!aParent->GetFirstChild(), "inserting into non-empty list");
1054 nsViewManager* vm = aParent->GetViewManager();
1055 while (aSibling) {
1056 nsView* next = aSibling->GetNextSibling();
1057 aSibling->SetNextSibling(nullptr);
1058 // true means 'after' in document order which is 'before' in view order,
1059 // so this call prepends the child, thus reversing the siblings as we go.
1060 vm->InsertChild(aParent, aSibling, nullptr, true);
1061 aSibling = next;
1065 nsresult
1066 nsSubDocumentFrame::BeginSwapDocShells(nsIFrame* aOther)
1068 if (!aOther || aOther->GetType() != nsGkAtoms::subDocumentFrame) {
1069 return NS_ERROR_NOT_IMPLEMENTED;
1072 nsSubDocumentFrame* other = static_cast<nsSubDocumentFrame*>(aOther);
1073 if (!mFrameLoader || !mDidCreateDoc || mCallingShow ||
1074 !other->mFrameLoader || !other->mDidCreateDoc) {
1075 return NS_ERROR_NOT_IMPLEMENTED;
1078 if (mInnerView && other->mInnerView) {
1079 nsView* ourSubdocViews = mInnerView->GetFirstChild();
1080 nsView* ourRemovedViews = ::BeginSwapDocShellsForViews(ourSubdocViews);
1081 nsView* otherSubdocViews = other->mInnerView->GetFirstChild();
1082 nsView* otherRemovedViews = ::BeginSwapDocShellsForViews(otherSubdocViews);
1084 ::InsertViewsInReverseOrder(ourRemovedViews, other->mInnerView);
1085 ::InsertViewsInReverseOrder(otherRemovedViews, mInnerView);
1087 mFrameLoader.swap(other->mFrameLoader);
1088 return NS_OK;
1091 static bool
1092 EndSwapDocShellsForDocument(nsIDocument* aDocument, void*)
1094 NS_PRECONDITION(aDocument, "");
1096 // Our docshell and view trees have been updated for the new hierarchy.
1097 // Now also update all nsDeviceContext::mWidget to that of the
1098 // container view in the new hierarchy.
1099 nsCOMPtr<nsIDocShell> ds = aDocument->GetDocShell();
1100 if (ds) {
1101 nsCOMPtr<nsIContentViewer> cv;
1102 ds->GetContentViewer(getter_AddRefs(cv));
1103 while (cv) {
1104 nsRefPtr<nsPresContext> pc;
1105 cv->GetPresContext(getter_AddRefs(pc));
1106 if (pc && pc->GetPresShell()) {
1107 pc->GetPresShell()->SetNeverPainting(ds->IsInvisible());
1109 nsDeviceContext* dc = pc ? pc->DeviceContext() : nullptr;
1110 if (dc) {
1111 nsView* v = cv->FindContainerView();
1112 dc->Init(v ? v->GetNearestWidget(nullptr) : nullptr);
1114 nsCOMPtr<nsIContentViewer> prev;
1115 cv->GetPreviousViewer(getter_AddRefs(prev));
1116 cv = prev;
1120 aDocument->EnumerateActivityObservers(
1121 nsObjectFrame::EndSwapDocShells, nullptr);
1122 aDocument->EnumerateSubDocuments(EndSwapDocShellsForDocument, nullptr);
1123 return true;
1126 static void
1127 EndSwapDocShellsForViews(nsView* aSibling)
1129 for ( ; aSibling; aSibling = aSibling->GetNextSibling()) {
1130 nsIDocument* doc = ::GetDocumentFromView(aSibling);
1131 if (doc) {
1132 ::EndSwapDocShellsForDocument(doc, nullptr);
1134 nsIFrame *frame = aSibling->GetFrame();
1135 if (frame) {
1136 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(frame);
1137 if (parent->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
1138 nsIFrame::AddInPopupStateBitToDescendants(frame);
1139 } else {
1140 nsIFrame::RemoveInPopupStateBitFromDescendants(frame);
1142 if (frame->HasInvalidFrameInSubtree()) {
1143 while (parent && !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
1144 parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
1145 parent = nsLayoutUtils::GetCrossDocParentFrame(parent);
1152 void
1153 nsSubDocumentFrame::EndSwapDocShells(nsIFrame* aOther)
1155 nsSubDocumentFrame* other = static_cast<nsSubDocumentFrame*>(aOther);
1156 nsWeakFrame weakThis(this);
1157 nsWeakFrame weakOther(aOther);
1159 if (mInnerView) {
1160 ::EndSwapDocShellsForViews(mInnerView->GetFirstChild());
1162 if (other->mInnerView) {
1163 ::EndSwapDocShellsForViews(other->mInnerView->GetFirstChild());
1166 // Now make sure we reflow both frames, in case their contents
1167 // determine their size.
1168 // And repaint them, for good measure, in case there's nothing
1169 // interesting that happens during reflow.
1170 if (weakThis.IsAlive()) {
1171 PresContext()->PresShell()->
1172 FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
1173 InvalidateFrameSubtree();
1175 if (weakOther.IsAlive()) {
1176 other->PresContext()->PresShell()->
1177 FrameNeedsReflow(other, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
1178 other->InvalidateFrameSubtree();
1182 nsView*
1183 nsSubDocumentFrame::EnsureInnerView()
1185 if (mInnerView) {
1186 return mInnerView;
1189 // create, init, set the parent of the view
1190 nsView* outerView = GetView();
1191 NS_ASSERTION(outerView, "Must have an outer view already");
1192 nsRect viewBounds(0, 0, 0, 0); // size will be fixed during reflow
1194 nsViewManager* viewMan = outerView->GetViewManager();
1195 nsView* innerView = viewMan->CreateView(viewBounds, outerView);
1196 if (!innerView) {
1197 NS_ERROR("Could not create inner view");
1198 return nullptr;
1200 mInnerView = innerView;
1201 viewMan->InsertChild(outerView, innerView, nullptr, true);
1203 return mInnerView;
1206 nsIFrame*
1207 nsSubDocumentFrame::ObtainIntrinsicSizeFrame()
1209 nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(GetContent());
1210 if (olc) {
1211 // We are an HTML <object>, <embed> or <applet> (a replaced element).
1213 // Try to get an nsIFrame for our sub-document's document element
1214 nsIFrame* subDocRoot = nullptr;
1216 nsCOMPtr<nsIDocShell> docShell;
1217 GetDocShell(getter_AddRefs(docShell));
1218 if (docShell) {
1219 nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
1220 if (presShell) {
1221 nsIScrollableFrame* scrollable = presShell->GetRootScrollFrameAsScrollable();
1222 if (scrollable) {
1223 nsIFrame* scrolled = scrollable->GetScrolledFrame();
1224 if (scrolled) {
1225 subDocRoot = scrolled->GetFirstPrincipalChild();
1231 if (subDocRoot && subDocRoot->GetContent() &&
1232 subDocRoot->GetContent()->NodeInfo()->Equals(nsGkAtoms::svg, kNameSpaceID_SVG)) {
1233 return subDocRoot; // SVG documents have an intrinsic size
1236 return nullptr;