Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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)
47 {
48 NS_PRECONDITION(aView, "");
50 nsIFrame* f = aView->GetFrame();
51 nsIPresShell* ps = f ? f->PresContext()->PresShell() : nullptr;
52 return ps ? ps->GetDocument() : nullptr;
53 }
55 nsSubDocumentFrame::nsSubDocumentFrame(nsStyleContext* aContext)
56 : nsLeafFrame(aContext)
57 , mIsInline(false)
58 , mPostedReflowCallback(false)
59 , mDidCreateDoc(false)
60 , mCallingShow(false)
61 {
62 }
64 #ifdef ACCESSIBILITY
65 a11y::AccType
66 nsSubDocumentFrame::AccessibleType()
67 {
68 return a11y::eOuterDocType;
69 }
70 #endif
72 NS_QUERYFRAME_HEAD(nsSubDocumentFrame)
73 NS_QUERYFRAME_ENTRY(nsSubDocumentFrame)
74 NS_QUERYFRAME_TAIL_INHERITING(nsLeafFrame)
76 class AsyncFrameInit : public nsRunnable
77 {
78 public:
79 AsyncFrameInit(nsIFrame* aFrame) : mFrame(aFrame) {}
80 NS_IMETHOD Run()
81 {
82 if (mFrame.IsAlive()) {
83 static_cast<nsSubDocumentFrame*>(mFrame.GetFrame())->ShowViewer();
84 }
85 return NS_OK;
86 }
87 private:
88 nsWeakFrame mFrame;
89 };
91 static void
92 InsertViewsInReverseOrder(nsView* aSibling, nsView* aParent);
94 static void
95 EndSwapDocShellsForViews(nsView* aView);
97 void
98 nsSubDocumentFrame::Init(nsIContent* aContent,
99 nsIFrame* aParent,
100 nsIFrame* aPrevInFlow)
101 {
102 // determine if we are a <frame> or <iframe>
103 if (aContent) {
104 nsCOMPtr<nsIDOMHTMLFrameElement> frameElem = do_QueryInterface(aContent);
105 mIsInline = frameElem ? false : true;
106 }
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);
120 }
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();
144 }
145 }
146 frameloader->SetDetachedSubdocView(nullptr, nullptr);
147 }
149 nsContentUtils::AddScriptRunner(new AsyncFrameInit(this));
150 }
152 void
153 nsSubDocumentFrame::ShowViewer()
154 {
155 if (mCallingShow) {
156 return;
157 }
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;
178 }
179 mCallingShow = false;
180 mDidCreateDoc = didCreateDoc;
181 }
182 }
183 }
185 nsIFrame*
186 nsSubDocumentFrame::GetSubdocumentRootFrame()
187 {
188 if (!mInnerView)
189 return nullptr;
190 nsView* subdocView = mInnerView->GetFirstChild();
191 return subdocView ? subdocView->GetFrame() : nullptr;
192 }
194 nsIntSize
195 nsSubDocumentFrame::GetSubdocumentSize()
196 {
197 if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
198 nsRefPtr<nsFrameLoader> frameloader = FrameLoader();
199 if (frameloader) {
200 nsCOMPtr<nsIDocument> oldContainerDoc;
201 nsView* detachedViews =
202 frameloader->GetDetachedSubdocView(getter_AddRefs(oldContainerDoc));
203 if (detachedViews) {
204 nsSize size = detachedViews->GetBounds().Size();
205 nsPresContext* presContext = detachedViews->GetFrame()->PresContext();
206 return nsIntSize(presContext->AppUnitsToDevPixels(size.width),
207 presContext->AppUnitsToDevPixels(size.height));
208 }
209 }
210 // Pick some default size for now. Using 10x10 because that's what the
211 // code used to do.
212 return nsIntSize(10, 10);
213 } else {
214 nsSize docSizeAppUnits;
215 nsPresContext* presContext = PresContext();
216 nsCOMPtr<nsIDOMHTMLFrameElement> frameElem =
217 do_QueryInterface(GetContent());
218 if (frameElem) {
219 docSizeAppUnits = GetSize();
220 } else {
221 docSizeAppUnits = GetContentRect().Size();
222 }
223 return nsIntSize(presContext->AppUnitsToDevPixels(docSizeAppUnits.width),
224 presContext->AppUnitsToDevPixels(docSizeAppUnits.height));
225 }
226 }
228 bool
229 nsSubDocumentFrame::PassPointerEventsToChildren()
230 {
231 // Limit use of mozpasspointerevents to documents with embedded:apps/chrome
232 // permission, because this could be used by the parent document to discover
233 // which parts of the subdocument are transparent to events (if subdocument
234 // uses pointer-events:none on its root element, which is admittedly
235 // unlikely)
236 if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozpasspointerevents)) {
237 if (PresContext()->IsChrome()) {
238 return true;
239 }
241 nsCOMPtr<nsIPermissionManager> permMgr =
242 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
243 if (permMgr) {
244 uint32_t permission = nsIPermissionManager::DENY_ACTION;
245 permMgr->TestPermissionFromPrincipal(GetContent()->NodePrincipal(),
246 "embed-apps", &permission);
248 return permission == nsIPermissionManager::ALLOW_ACTION;
249 }
250 }
252 return false;
253 }
255 static void
256 WrapBackgroundColorInOwnLayer(nsDisplayListBuilder* aBuilder,
257 nsIFrame* aFrame,
258 nsDisplayList* aList)
259 {
260 nsDisplayList tempItems;
261 nsDisplayItem* item;
262 while ((item = aList->RemoveBottom()) != nullptr) {
263 if (item->GetType() == nsDisplayItem::TYPE_BACKGROUND_COLOR) {
264 nsDisplayList tmpList;
265 tmpList.AppendToTop(item);
266 item = new (aBuilder) nsDisplayOwnLayer(aBuilder, aFrame, &tmpList);
267 }
268 tempItems.AppendToTop(item);
269 }
270 aList->AppendToTop(&tempItems);
271 }
273 void
274 nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
275 const nsRect& aDirtyRect,
276 const nsDisplayListSet& aLists)
277 {
278 if (!IsVisibleForPainting(aBuilder))
279 return;
281 nsFrameLoader* frameLoader = FrameLoader();
282 RenderFrameParent* rfp = nullptr;
283 if (frameLoader) {
284 rfp = frameLoader->GetCurrentRemoteFrame();
285 }
287 // If we are pointer-events:none then we don't need to HitTest background
288 bool pointerEventsNone = StyleVisibility()->mPointerEvents == NS_STYLE_POINTER_EVENTS_NONE;
289 if (!aBuilder->IsForEventDelivery() || !pointerEventsNone) {
290 nsDisplayListCollection decorations;
291 DisplayBorderBackgroundOutline(aBuilder, decorations);
292 if (rfp) {
293 // Wrap background colors of <iframe>s with remote subdocuments in their
294 // own layer so we generate a ColorLayer. This is helpful for optimizing
295 // compositing; we can skip compositing the ColorLayer when the
296 // remote content is opaque.
297 WrapBackgroundColorInOwnLayer(aBuilder, this, decorations.BorderBackground());
298 }
299 decorations.MoveTo(aLists);
300 }
302 bool passPointerEventsToChildren = false;
303 if (aBuilder->IsForEventDelivery()) {
304 passPointerEventsToChildren = PassPointerEventsToChildren();
305 // If mozpasspointerevents is set, then we should allow subdocument content
306 // to handle events even if we're pointer-events:none.
307 if (pointerEventsNone && !passPointerEventsToChildren) {
308 return;
309 }
310 }
312 // If we're passing pointer events to children then we have to descend into
313 // subdocuments no matter what, to determine which parts are transparent for
314 // elementFromPoint.
315 if (!mInnerView ||
316 (!aBuilder->GetDescendIntoSubdocuments() && !passPointerEventsToChildren)) {
317 return;
318 }
320 if (rfp) {
321 rfp->BuildDisplayList(aBuilder, this, aDirtyRect, aLists);
322 return;
323 }
325 nsView* subdocView = mInnerView->GetFirstChild();
326 if (!subdocView)
327 return;
329 nsCOMPtr<nsIPresShell> presShell = nullptr;
331 nsIFrame* subdocRootFrame = subdocView->GetFrame();
332 if (subdocRootFrame) {
333 presShell = subdocRootFrame->PresContext()->PresShell();
334 }
335 // If painting is suppressed in the presshell, we try to look for a better
336 // presshell to use.
337 if (!presShell || (presShell->IsPaintingSuppressed() &&
338 !aBuilder->IsIgnoringPaintSuppression())) {
339 // During page transition mInnerView will sometimes have two children, the
340 // first being the new page that may not have any frame, and the second
341 // being the old page that will probably have a frame.
342 nsView* nextView = subdocView->GetNextSibling();
343 nsIFrame* frame = nullptr;
344 if (nextView) {
345 frame = nextView->GetFrame();
346 }
347 if (frame) {
348 nsIPresShell* ps = frame->PresContext()->PresShell();
349 if (!presShell || (ps && !ps->IsPaintingSuppressed())) {
350 subdocView = nextView;
351 subdocRootFrame = frame;
352 presShell = ps;
353 }
354 }
355 if (!presShell) {
356 // If we don't have a frame we use this roundabout way to get the pres shell.
357 if (!mFrameLoader)
358 return;
359 nsCOMPtr<nsIDocShell> docShell;
360 mFrameLoader->GetDocShell(getter_AddRefs(docShell));
361 if (!docShell)
362 return;
363 presShell = docShell->GetPresShell();
364 if (!presShell)
365 return;
366 }
367 }
369 nsPresContext* presContext = presShell->GetPresContext();
371 int32_t parentAPD = PresContext()->AppUnitsPerDevPixel();
372 int32_t subdocAPD = presContext->AppUnitsPerDevPixel();
374 nsRect dirty;
375 bool haveDisplayPort = false;
376 bool ignoreViewportScrolling = false;
377 nsIFrame* savedIgnoreScrollFrame = nullptr;
378 if (subdocRootFrame) {
379 // get the dirty rect relative to the root frame of the subdoc
380 dirty = aDirtyRect + GetOffsetToCrossDoc(subdocRootFrame);
381 // and convert into the appunits of the subdoc
382 dirty = dirty.ConvertAppUnitsRoundOut(parentAPD, subdocAPD);
384 if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
385 // for root content documents we want the base to be the composition bounds
386 nsRect displayportBase = presContext->IsRootContentDocument() ?
387 nsRect(nsPoint(0,0), nsLayoutUtils::CalculateCompositionSizeForFrame(rootScrollFrame)) :
388 dirty.Intersect(nsRect(nsPoint(0,0), subdocRootFrame->GetSize()));
389 nsRect displayPort;
390 if (nsLayoutUtils::GetOrMaybeCreateDisplayPort(
391 *aBuilder, rootScrollFrame, displayportBase, &displayPort)) {
392 haveDisplayPort = true;
393 dirty = displayPort;
394 }
396 ignoreViewportScrolling = presShell->IgnoringViewportScrolling();
397 if (ignoreViewportScrolling) {
398 savedIgnoreScrollFrame = aBuilder->GetIgnoreScrollFrame();
399 aBuilder->SetIgnoreScrollFrame(rootScrollFrame);
400 }
401 }
403 aBuilder->EnterPresShell(subdocRootFrame, dirty);
404 }
406 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
407 if (ShouldClipSubdocument()) {
408 clipState.ClipContainingBlockDescendantsToContentBox(aBuilder, this);
409 }
411 nsIScrollableFrame *sf = presShell->GetRootScrollFrameAsScrollable();
412 bool constructResolutionItem = subdocRootFrame &&
413 (presShell->GetXResolution() != 1.0 || presShell->GetYResolution() != 1.0);
414 bool constructZoomItem = subdocRootFrame && parentAPD != subdocAPD;
415 bool needsOwnLayer = constructResolutionItem || constructZoomItem ||
416 haveDisplayPort ||
417 presContext->IsRootContentDocument() || (sf && sf->IsScrollingActive());
419 // Don't let in fixed pos propagate down to child documents. This makes
420 // it a little less effective but doesn't regress an important case of a
421 // child document being in a fixed pos element where we would do no occlusion
422 // at all if we let it propagate down.
423 nsDisplayListBuilder::AutoInFixedPosSetter
424 buildingInFixedPos(aBuilder, false);
426 nsDisplayList childItems;
428 {
429 DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
430 if (needsOwnLayer) {
431 // Clear current clip. There's no point in propagating it down, since
432 // the layer we will construct will be clipped by the current clip.
433 // In fact for nsDisplayZoom propagating it down would be incorrect since
434 // nsDisplayZoom changes the meaning of appunits.
435 nestedClipState.Clear();
436 }
438 if (subdocRootFrame) {
439 nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(
440 aBuilder,
441 ignoreViewportScrolling && subdocRootFrame->GetContent()
442 ? nsLayoutUtils::FindOrCreateIDFor(subdocRootFrame->GetContent())
443 : aBuilder->GetCurrentScrollParentId());
445 aBuilder->SetAncestorHasTouchEventHandler(false);
446 subdocRootFrame->
447 BuildDisplayListForStackingContext(aBuilder, dirty, &childItems);
448 }
450 if (!aBuilder->IsForEventDelivery()) {
451 // If we are going to use a displayzoom below then any items we put under
452 // it need to have underlying frames from the subdocument. So we need to
453 // calculate the bounds based on which frame will be the underlying frame
454 // for the canvas background color item.
455 nsRect bounds = GetContentRectRelativeToSelf() +
456 aBuilder->ToReferenceFrame(this);
457 if (subdocRootFrame) {
458 bounds = bounds.ConvertAppUnitsRoundOut(parentAPD, subdocAPD);
459 }
461 // If we are in print preview/page layout we want to paint the grey
462 // background behind the page, not the canvas color. The canvas color gets
463 // painted on the page itself.
464 if (nsLayoutUtils::NeedsPrintPreviewBackground(presContext)) {
465 presShell->AddPrintPreviewBackgroundItem(
466 *aBuilder, childItems, subdocRootFrame ? subdocRootFrame : this,
467 bounds);
468 } else {
469 // Add the canvas background color to the bottom of the list. This
470 // happens after we've built the list so that AddCanvasBackgroundColorItem
471 // can monkey with the contents if necessary.
472 uint32_t flags = nsIPresShell::FORCE_DRAW;
473 presShell->AddCanvasBackgroundColorItem(
474 *aBuilder, childItems, subdocRootFrame ? subdocRootFrame : this,
475 bounds, NS_RGBA(0,0,0,0), flags);
476 }
477 }
478 }
480 // Generate a resolution and/or zoom item if needed. If one or both of those is
481 // created, we don't need to create a separate nsDisplaySubDocument.
483 uint32_t flags = nsDisplayOwnLayer::GENERATE_SUBDOC_INVALIDATIONS;
484 // If ignoreViewportScrolling is true then the top most layer we create here
485 // is going to become the scrollable layer for the root scroll frame, so we
486 // want to add nsDisplayOwnLayer::GENERATE_SCROLLABLE_LAYER to whatever layer
487 // becomes the topmost. We do this below.
488 if (constructZoomItem) {
489 uint32_t zoomFlags = flags;
490 if (ignoreViewportScrolling && !constructResolutionItem) {
491 zoomFlags |= nsDisplayOwnLayer::GENERATE_SCROLLABLE_LAYER;
492 }
493 nsDisplayZoom* zoomItem =
494 new (aBuilder) nsDisplayZoom(aBuilder, subdocRootFrame, &childItems,
495 subdocAPD, parentAPD, zoomFlags);
496 childItems.AppendToTop(zoomItem);
497 needsOwnLayer = false;
498 }
499 // Wrap the zoom item in the resolution item if we have both because we want the
500 // resolution scale applied on top of the app units per dev pixel conversion.
501 if (ignoreViewportScrolling) {
502 flags |= nsDisplayOwnLayer::GENERATE_SCROLLABLE_LAYER;
503 }
504 if (constructResolutionItem) {
505 nsDisplayResolution* resolutionItem =
506 new (aBuilder) nsDisplayResolution(aBuilder, subdocRootFrame, &childItems,
507 flags);
508 childItems.AppendToTop(resolutionItem);
509 needsOwnLayer = false;
510 }
511 if (needsOwnLayer) {
512 // We always want top level content documents to be in their own layer.
513 nsDisplaySubDocument* layerItem = new (aBuilder) nsDisplaySubDocument(
514 aBuilder, subdocRootFrame ? subdocRootFrame : this,
515 &childItems, flags);
516 childItems.AppendToTop(layerItem);
517 }
519 if (subdocRootFrame) {
520 aBuilder->LeavePresShell(subdocRootFrame, dirty);
522 if (ignoreViewportScrolling) {
523 aBuilder->SetIgnoreScrollFrame(savedIgnoreScrollFrame);
524 }
525 }
527 if (aBuilder->IsForImageVisibility()) {
528 // We don't add the childItems to the return list as we're dealing with them here.
529 presShell->RebuildImageVisibility(childItems);
530 childItems.DeleteAll();
531 } else {
532 aLists.Content()->AppendToTop(&childItems);
533 }
534 }
536 nscoord
537 nsSubDocumentFrame::GetIntrinsicWidth()
538 {
539 if (!IsInline()) {
540 return 0; // HTML <frame> has no useful intrinsic width
541 }
543 if (mContent->IsXUL()) {
544 return 0; // XUL <iframe> and <browser> have no useful intrinsic width
545 }
547 NS_ASSERTION(ObtainIntrinsicSizeFrame() == nullptr,
548 "Intrinsic width should come from the embedded document.");
550 // We must be an HTML <iframe>. Default to a width of 300, for IE
551 // compat (and per CSS2.1 draft).
552 return nsPresContext::CSSPixelsToAppUnits(300);
553 }
555 nscoord
556 nsSubDocumentFrame::GetIntrinsicHeight()
557 {
558 // <frame> processing does not use this routine, only <iframe>
559 NS_ASSERTION(IsInline(), "Shouldn't have been called");
561 if (mContent->IsXUL()) {
562 return 0;
563 }
565 NS_ASSERTION(ObtainIntrinsicSizeFrame() == nullptr,
566 "Intrinsic height should come from the embedded document.");
568 // Use 150px, for compatibility with IE, and per CSS2.1 draft.
569 return nsPresContext::CSSPixelsToAppUnits(150);
570 }
572 #ifdef DEBUG_FRAME_DUMP
573 void
574 nsSubDocumentFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
575 {
576 nsCString str;
577 ListGeneric(str, aPrefix, aFlags);
578 fprintf_stderr(out, "%s\n", str.get());
580 if (aFlags & TRAVERSE_SUBDOCUMENT_FRAMES) {
581 nsSubDocumentFrame* f = const_cast<nsSubDocumentFrame*>(this);
582 nsIFrame* subdocRootFrame = f->GetSubdocumentRootFrame();
583 if (subdocRootFrame) {
584 nsCString pfx(aPrefix);
585 pfx += " ";
586 subdocRootFrame->List(out, pfx.get(), aFlags);
587 }
588 }
589 }
591 nsresult nsSubDocumentFrame::GetFrameName(nsAString& aResult) const
592 {
593 return MakeFrameName(NS_LITERAL_STRING("FrameOuter"), aResult);
594 }
595 #endif
597 nsIAtom*
598 nsSubDocumentFrame::GetType() const
599 {
600 return nsGkAtoms::subDocumentFrame;
601 }
603 /* virtual */ nscoord
604 nsSubDocumentFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
605 {
606 nscoord result;
607 DISPLAY_MIN_WIDTH(this, result);
609 nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
610 if (subDocRoot) {
611 result = subDocRoot->GetMinWidth(aRenderingContext);
612 } else {
613 result = GetIntrinsicWidth();
614 }
616 return result;
617 }
619 /* virtual */ nscoord
620 nsSubDocumentFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
621 {
622 nscoord result;
623 DISPLAY_PREF_WIDTH(this, result);
625 nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
626 if (subDocRoot) {
627 result = subDocRoot->GetPrefWidth(aRenderingContext);
628 } else {
629 result = GetIntrinsicWidth();
630 }
632 return result;
633 }
635 /* virtual */ IntrinsicSize
636 nsSubDocumentFrame::GetIntrinsicSize()
637 {
638 nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
639 if (subDocRoot) {
640 return subDocRoot->GetIntrinsicSize();
641 }
642 return nsLeafFrame::GetIntrinsicSize();
643 }
645 /* virtual */ nsSize
646 nsSubDocumentFrame::GetIntrinsicRatio()
647 {
648 nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
649 if (subDocRoot) {
650 return subDocRoot->GetIntrinsicRatio();
651 }
652 return nsLeafFrame::GetIntrinsicRatio();
653 }
655 /* virtual */ nsSize
656 nsSubDocumentFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
657 nsSize aCBSize, nscoord aAvailableWidth,
658 nsSize aMargin, nsSize aBorder,
659 nsSize aPadding, bool aShrinkWrap)
660 {
661 if (!IsInline()) {
662 return nsFrame::ComputeAutoSize(aRenderingContext, aCBSize,
663 aAvailableWidth, aMargin, aBorder,
664 aPadding, aShrinkWrap);
665 }
667 return nsLeafFrame::ComputeAutoSize(aRenderingContext, aCBSize,
668 aAvailableWidth, aMargin, aBorder,
669 aPadding, aShrinkWrap);
670 }
673 /* virtual */ nsSize
674 nsSubDocumentFrame::ComputeSize(nsRenderingContext *aRenderingContext,
675 nsSize aCBSize, nscoord aAvailableWidth,
676 nsSize aMargin, nsSize aBorder, nsSize aPadding,
677 uint32_t aFlags)
678 {
679 nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
680 if (subDocRoot) {
681 return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(
682 aRenderingContext, this,
683 subDocRoot->GetIntrinsicSize(),
684 subDocRoot->GetIntrinsicRatio(),
685 aCBSize, aMargin, aBorder, aPadding);
686 }
687 return nsLeafFrame::ComputeSize(aRenderingContext, aCBSize, aAvailableWidth,
688 aMargin, aBorder, aPadding, aFlags);
689 }
691 nsresult
692 nsSubDocumentFrame::Reflow(nsPresContext* aPresContext,
693 nsHTMLReflowMetrics& aDesiredSize,
694 const nsHTMLReflowState& aReflowState,
695 nsReflowStatus& aStatus)
696 {
697 DO_GLOBAL_REFLOW_COUNT("nsSubDocumentFrame");
698 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
699 // printf("OuterFrame::Reflow %X (%d,%d) \n", this, aReflowState.AvailableWidth(), aReflowState.AvailableHeight());
700 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
701 ("enter nsSubDocumentFrame::Reflow: maxSize=%d,%d",
702 aReflowState.AvailableWidth(), aReflowState.AvailableHeight()));
704 aStatus = NS_FRAME_COMPLETE;
706 NS_ASSERTION(mContent->GetPrimaryFrame() == this,
707 "Shouldn't happen");
709 // XUL <iframe> or <browser>, or HTML <iframe>, <object> or <embed>
710 nsresult rv = nsLeafFrame::DoReflow(aPresContext, aDesiredSize, aReflowState,
711 aStatus);
712 NS_ENSURE_SUCCESS(rv, rv);
714 // "offset" is the offset of our content area from our frame's
715 // top-left corner.
716 nsPoint offset = nsPoint(aReflowState.ComputedPhysicalBorderPadding().left,
717 aReflowState.ComputedPhysicalBorderPadding().top);
719 nsSize innerSize(aDesiredSize.Width(), aDesiredSize.Height());
720 innerSize.width -= aReflowState.ComputedPhysicalBorderPadding().LeftRight();
721 innerSize.height -= aReflowState.ComputedPhysicalBorderPadding().TopBottom();
723 if (mInnerView) {
724 nsViewManager* vm = mInnerView->GetViewManager();
725 vm->MoveViewTo(mInnerView, offset.x, offset.y);
726 vm->ResizeView(mInnerView, nsRect(nsPoint(0, 0), innerSize), true);
727 }
729 aDesiredSize.SetOverflowAreasToDesiredBounds();
730 if (!ShouldClipSubdocument()) {
731 nsIFrame* subdocRootFrame = GetSubdocumentRootFrame();
732 if (subdocRootFrame) {
733 aDesiredSize.mOverflowAreas.UnionWith(subdocRootFrame->GetOverflowAreas() + offset);
734 }
735 }
737 FinishAndStoreOverflow(&aDesiredSize);
739 if (!aPresContext->IsPaginated() && !mPostedReflowCallback) {
740 PresContext()->PresShell()->PostReflowCallback(this);
741 mPostedReflowCallback = true;
742 }
744 // printf("OuterFrame::Reflow DONE %X (%d,%d)\n", this,
745 // aDesiredSize.Width(), aDesiredSize.Height());
747 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
748 ("exit nsSubDocumentFrame::Reflow: size=%d,%d status=%x",
749 aDesiredSize.Width(), aDesiredSize.Height(), aStatus));
751 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
752 return NS_OK;
753 }
755 bool
756 nsSubDocumentFrame::ReflowFinished()
757 {
758 if (mFrameLoader) {
759 nsWeakFrame weakFrame(this);
761 mFrameLoader->UpdatePositionAndSize(this);
763 if (weakFrame.IsAlive()) {
764 // Make sure that we can post a reflow callback in the future.
765 mPostedReflowCallback = false;
766 }
767 } else {
768 mPostedReflowCallback = false;
769 }
770 return false;
771 }
773 void
774 nsSubDocumentFrame::ReflowCallbackCanceled()
775 {
776 mPostedReflowCallback = false;
777 }
779 nsresult
780 nsSubDocumentFrame::AttributeChanged(int32_t aNameSpaceID,
781 nsIAtom* aAttribute,
782 int32_t aModType)
783 {
784 if (aNameSpaceID != kNameSpaceID_None) {
785 return NS_OK;
786 }
788 // If the noResize attribute changes, dis/allow frame to be resized
789 if (aAttribute == nsGkAtoms::noresize) {
790 // Note that we're not doing content type checks, but that's ok -- if
791 // they'd fail we will just end up with a null framesetFrame.
792 if (mContent->GetParent()->Tag() == nsGkAtoms::frameset) {
793 nsIFrame* parentFrame = GetParent();
795 if (parentFrame) {
796 // There is no interface for nsHTMLFramesetFrame so QI'ing to
797 // concrete class, yay!
798 nsHTMLFramesetFrame* framesetFrame = do_QueryFrame(parentFrame);
799 if (framesetFrame) {
800 framesetFrame->RecalculateBorderResize();
801 }
802 }
803 }
804 }
805 else if (aAttribute == nsGkAtoms::showresizer) {
806 nsIFrame* rootFrame = GetSubdocumentRootFrame();
807 if (rootFrame) {
808 rootFrame->PresContext()->PresShell()->
809 FrameNeedsReflow(rootFrame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
810 }
811 }
812 else if (aAttribute == nsGkAtoms::marginwidth ||
813 aAttribute == nsGkAtoms::marginheight) {
815 // Retrieve the attributes
816 nsIntSize margins = GetMarginAttributes();
818 // Notify the frameloader
819 nsRefPtr<nsFrameLoader> frameloader = FrameLoader();
820 if (frameloader)
821 frameloader->MarginsChanged(margins.width, margins.height);
822 }
824 return NS_OK;
825 }
827 nsIFrame*
828 NS_NewSubDocumentFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
829 {
830 return new (aPresShell) nsSubDocumentFrame(aContext);
831 }
833 NS_IMPL_FRAMEARENA_HELPERS(nsSubDocumentFrame)
835 class nsHideViewer : public nsRunnable {
836 public:
837 nsHideViewer(nsIContent* aFrameElement,
838 nsFrameLoader* aFrameLoader,
839 nsIPresShell* aPresShell,
840 bool aHideViewerIfFrameless)
841 : mFrameElement(aFrameElement),
842 mFrameLoader(aFrameLoader),
843 mPresShell(aPresShell),
844 mHideViewerIfFrameless(aHideViewerIfFrameless)
845 {
846 NS_ASSERTION(mFrameElement, "Must have a frame element");
847 NS_ASSERTION(mFrameLoader, "Must have a frame loader");
848 NS_ASSERTION(mPresShell, "Must have a presshell");
849 }
851 NS_IMETHOD Run()
852 {
853 // Flush frames, to ensure any pending display:none changes are made.
854 // Note it can be unsafe to flush if we've destroyed the presentation
855 // for some other reason, like if we're shutting down.
856 if (!mPresShell->IsDestroying()) {
857 mPresShell->FlushPendingNotifications(Flush_Frames);
858 }
859 nsIFrame* frame = mFrameElement->GetPrimaryFrame();
860 if ((!frame && mHideViewerIfFrameless) ||
861 mPresShell->IsDestroying()) {
862 // Either the frame element has no nsIFrame or the presshell is being
863 // destroyed. Hide the nsFrameLoader, which destroys the presentation,
864 // and clear our references to the stashed presentation.
865 mFrameLoader->SetDetachedSubdocView(nullptr, nullptr);
866 mFrameLoader->Hide();
867 }
868 return NS_OK;
869 }
870 private:
871 nsCOMPtr<nsIContent> mFrameElement;
872 nsRefPtr<nsFrameLoader> mFrameLoader;
873 nsCOMPtr<nsIPresShell> mPresShell;
874 bool mHideViewerIfFrameless;
875 };
877 static nsView*
878 BeginSwapDocShellsForViews(nsView* aSibling);
880 void
881 nsSubDocumentFrame::DestroyFrom(nsIFrame* aDestructRoot)
882 {
883 if (mPostedReflowCallback) {
884 PresContext()->PresShell()->CancelReflowCallback(this);
885 mPostedReflowCallback = false;
886 }
888 // Detach the subdocument's views and stash them in the frame loader.
889 // We can then reattach them if we're being reframed (for example if
890 // the frame has been made position:fixed).
891 nsFrameLoader* frameloader = FrameLoader();
892 if (frameloader) {
893 nsView* detachedViews = ::BeginSwapDocShellsForViews(mInnerView->GetFirstChild());
894 frameloader->SetDetachedSubdocView(detachedViews, mContent->OwnerDoc());
896 // We call nsFrameLoader::HideViewer() in a script runner so that we can
897 // safely determine whether the frame is being reframed or destroyed.
898 nsContentUtils::AddScriptRunner(
899 new nsHideViewer(mContent,
900 mFrameLoader,
901 PresContext()->PresShell(),
902 (mDidCreateDoc || mCallingShow)));
903 }
905 nsLeafFrame::DestroyFrom(aDestructRoot);
906 }
908 nsIntSize
909 nsSubDocumentFrame::GetMarginAttributes()
910 {
911 nsIntSize result(-1, -1);
912 nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent);
913 if (content) {
914 const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::marginwidth);
915 if (attr && attr->Type() == nsAttrValue::eInteger)
916 result.width = attr->GetIntegerValue();
917 attr = content->GetParsedAttr(nsGkAtoms::marginheight);
918 if (attr && attr->Type() == nsAttrValue::eInteger)
919 result.height = attr->GetIntegerValue();
920 }
921 return result;
922 }
924 nsFrameLoader*
925 nsSubDocumentFrame::FrameLoader()
926 {
927 nsIContent* content = GetContent();
928 if (!content)
929 return nullptr;
931 if (!mFrameLoader) {
932 nsCOMPtr<nsIFrameLoaderOwner> loaderOwner = do_QueryInterface(content);
933 if (loaderOwner) {
934 nsCOMPtr<nsIFrameLoader> loader;
935 loaderOwner->GetFrameLoader(getter_AddRefs(loader));
936 mFrameLoader = static_cast<nsFrameLoader*>(loader.get());
937 }
938 }
939 return mFrameLoader;
940 }
942 // XXX this should be called ObtainDocShell or something like that,
943 // to indicate that it could have side effects
944 nsresult
945 nsSubDocumentFrame::GetDocShell(nsIDocShell **aDocShell)
946 {
947 *aDocShell = nullptr;
949 NS_ENSURE_STATE(FrameLoader());
950 return mFrameLoader->GetDocShell(aDocShell);
951 }
953 static void
954 DestroyDisplayItemDataForFrames(nsIFrame* aFrame)
955 {
956 FrameLayerBuilder::DestroyDisplayItemDataFor(aFrame);
958 nsIFrame::ChildListIterator lists(aFrame);
959 for (; !lists.IsDone(); lists.Next()) {
960 nsFrameList::Enumerator childFrames(lists.CurrentList());
961 for (; !childFrames.AtEnd(); childFrames.Next()) {
962 DestroyDisplayItemDataForFrames(childFrames.get());
963 }
964 }
965 }
967 static bool
968 BeginSwapDocShellsForDocument(nsIDocument* aDocument, void*)
969 {
970 NS_PRECONDITION(aDocument, "");
972 nsIPresShell* shell = aDocument->GetShell();
973 if (shell) {
974 // Disable painting while the views are detached, see bug 946929.
975 shell->SetNeverPainting(true);
977 nsIFrame* rootFrame = shell->GetRootFrame();
978 if (rootFrame) {
979 ::DestroyDisplayItemDataForFrames(rootFrame);
980 }
981 }
982 aDocument->EnumerateFreezableElements(
983 nsObjectFrame::BeginSwapDocShells, nullptr);
984 aDocument->EnumerateSubDocuments(BeginSwapDocShellsForDocument, nullptr);
985 return true;
986 }
988 static nsView*
989 BeginSwapDocShellsForViews(nsView* aSibling)
990 {
991 // Collect the removed sibling views in reverse order in 'removedViews'.
992 nsView* removedViews = nullptr;
993 while (aSibling) {
994 nsIDocument* doc = ::GetDocumentFromView(aSibling);
995 if (doc) {
996 ::BeginSwapDocShellsForDocument(doc, nullptr);
997 }
998 nsView* next = aSibling->GetNextSibling();
999 aSibling->GetViewManager()->RemoveChild(aSibling);
1000 aSibling->SetNextSibling(removedViews);
1001 removedViews = aSibling;
1002 aSibling = next;
1003 }
1004 return removedViews;
1005 }
1007 static void
1008 InsertViewsInReverseOrder(nsView* aSibling, nsView* aParent)
1009 {
1010 NS_PRECONDITION(aParent, "");
1011 NS_PRECONDITION(!aParent->GetFirstChild(), "inserting into non-empty list");
1013 nsViewManager* vm = aParent->GetViewManager();
1014 while (aSibling) {
1015 nsView* next = aSibling->GetNextSibling();
1016 aSibling->SetNextSibling(nullptr);
1017 // true means 'after' in document order which is 'before' in view order,
1018 // so this call prepends the child, thus reversing the siblings as we go.
1019 vm->InsertChild(aParent, aSibling, nullptr, true);
1020 aSibling = next;
1021 }
1022 }
1024 nsresult
1025 nsSubDocumentFrame::BeginSwapDocShells(nsIFrame* aOther)
1026 {
1027 if (!aOther || aOther->GetType() != nsGkAtoms::subDocumentFrame) {
1028 return NS_ERROR_NOT_IMPLEMENTED;
1029 }
1031 nsSubDocumentFrame* other = static_cast<nsSubDocumentFrame*>(aOther);
1032 if (!mFrameLoader || !mDidCreateDoc || mCallingShow ||
1033 !other->mFrameLoader || !other->mDidCreateDoc) {
1034 return NS_ERROR_NOT_IMPLEMENTED;
1035 }
1037 if (mInnerView && other->mInnerView) {
1038 nsView* ourSubdocViews = mInnerView->GetFirstChild();
1039 nsView* ourRemovedViews = ::BeginSwapDocShellsForViews(ourSubdocViews);
1040 nsView* otherSubdocViews = other->mInnerView->GetFirstChild();
1041 nsView* otherRemovedViews = ::BeginSwapDocShellsForViews(otherSubdocViews);
1043 ::InsertViewsInReverseOrder(ourRemovedViews, other->mInnerView);
1044 ::InsertViewsInReverseOrder(otherRemovedViews, mInnerView);
1045 }
1046 mFrameLoader.swap(other->mFrameLoader);
1047 return NS_OK;
1048 }
1050 static bool
1051 EndSwapDocShellsForDocument(nsIDocument* aDocument, void*)
1052 {
1053 NS_PRECONDITION(aDocument, "");
1055 // Our docshell and view trees have been updated for the new hierarchy.
1056 // Now also update all nsDeviceContext::mWidget to that of the
1057 // container view in the new hierarchy.
1058 nsCOMPtr<nsIDocShell> ds = aDocument->GetDocShell();
1059 if (ds) {
1060 nsCOMPtr<nsIContentViewer> cv;
1061 ds->GetContentViewer(getter_AddRefs(cv));
1062 while (cv) {
1063 nsRefPtr<nsPresContext> pc;
1064 cv->GetPresContext(getter_AddRefs(pc));
1065 if (pc && pc->GetPresShell()) {
1066 pc->GetPresShell()->SetNeverPainting(ds->IsInvisible());
1067 }
1068 nsDeviceContext* dc = pc ? pc->DeviceContext() : nullptr;
1069 if (dc) {
1070 nsView* v = cv->FindContainerView();
1071 dc->Init(v ? v->GetNearestWidget(nullptr) : nullptr);
1072 }
1073 nsCOMPtr<nsIContentViewer> prev;
1074 cv->GetPreviousViewer(getter_AddRefs(prev));
1075 cv = prev;
1076 }
1077 }
1079 aDocument->EnumerateFreezableElements(
1080 nsObjectFrame::EndSwapDocShells, nullptr);
1081 aDocument->EnumerateSubDocuments(EndSwapDocShellsForDocument, nullptr);
1082 return true;
1083 }
1085 static void
1086 EndSwapDocShellsForViews(nsView* aSibling)
1087 {
1088 for ( ; aSibling; aSibling = aSibling->GetNextSibling()) {
1089 nsIDocument* doc = ::GetDocumentFromView(aSibling);
1090 if (doc) {
1091 ::EndSwapDocShellsForDocument(doc, nullptr);
1092 }
1093 nsIFrame *frame = aSibling->GetFrame();
1094 if (frame) {
1095 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(frame);
1096 if (parent->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
1097 nsIFrame::AddInPopupStateBitToDescendants(frame);
1098 } else {
1099 nsIFrame::RemoveInPopupStateBitFromDescendants(frame);
1100 }
1101 if (frame->HasInvalidFrameInSubtree()) {
1102 while (parent && !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
1103 parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
1104 parent = nsLayoutUtils::GetCrossDocParentFrame(parent);
1105 }
1106 }
1107 }
1108 }
1109 }
1111 void
1112 nsSubDocumentFrame::EndSwapDocShells(nsIFrame* aOther)
1113 {
1114 nsSubDocumentFrame* other = static_cast<nsSubDocumentFrame*>(aOther);
1115 nsWeakFrame weakThis(this);
1116 nsWeakFrame weakOther(aOther);
1118 if (mInnerView) {
1119 ::EndSwapDocShellsForViews(mInnerView->GetFirstChild());
1120 }
1121 if (other->mInnerView) {
1122 ::EndSwapDocShellsForViews(other->mInnerView->GetFirstChild());
1123 }
1125 // Now make sure we reflow both frames, in case their contents
1126 // determine their size.
1127 // And repaint them, for good measure, in case there's nothing
1128 // interesting that happens during reflow.
1129 if (weakThis.IsAlive()) {
1130 PresContext()->PresShell()->
1131 FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
1132 InvalidateFrameSubtree();
1133 }
1134 if (weakOther.IsAlive()) {
1135 other->PresContext()->PresShell()->
1136 FrameNeedsReflow(other, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
1137 other->InvalidateFrameSubtree();
1138 }
1139 }
1141 nsView*
1142 nsSubDocumentFrame::EnsureInnerView()
1143 {
1144 if (mInnerView) {
1145 return mInnerView;
1146 }
1148 // create, init, set the parent of the view
1149 nsView* outerView = GetView();
1150 NS_ASSERTION(outerView, "Must have an outer view already");
1151 nsRect viewBounds(0, 0, 0, 0); // size will be fixed during reflow
1153 nsViewManager* viewMan = outerView->GetViewManager();
1154 nsView* innerView = viewMan->CreateView(viewBounds, outerView);
1155 if (!innerView) {
1156 NS_ERROR("Could not create inner view");
1157 return nullptr;
1158 }
1159 mInnerView = innerView;
1160 viewMan->InsertChild(outerView, innerView, nullptr, true);
1162 return mInnerView;
1163 }
1165 nsIFrame*
1166 nsSubDocumentFrame::ObtainIntrinsicSizeFrame()
1167 {
1168 nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(GetContent());
1169 if (olc) {
1170 // We are an HTML <object>, <embed> or <applet> (a replaced element).
1172 // Try to get an nsIFrame for our sub-document's document element
1173 nsIFrame* subDocRoot = nullptr;
1175 nsCOMPtr<nsIDocShell> docShell;
1176 GetDocShell(getter_AddRefs(docShell));
1177 if (docShell) {
1178 nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
1179 if (presShell) {
1180 nsIScrollableFrame* scrollable = presShell->GetRootScrollFrameAsScrollable();
1181 if (scrollable) {
1182 nsIFrame* scrolled = scrollable->GetScrolledFrame();
1183 if (scrolled) {
1184 subDocRoot = scrolled->GetFirstPrincipalChild();
1185 }
1186 }
1187 }
1188 }
1190 if (subDocRoot && subDocRoot->GetContent() &&
1191 subDocRoot->GetContent()->NodeInfo()->Equals(nsGkAtoms::svg, kNameSpaceID_SVG)) {
1192 return subDocRoot; // SVG documents have an intrinsic size
1193 }
1194 }
1195 return nullptr;
1196 }