/src/mozilla-central/layout/generic/nsSubDocumentFrame.cpp
Line | Count | Source (jump to first uncovered line) |
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 "gfxPrefs.h" |
15 | | |
16 | | #include "mozilla/layout/RenderFrameParent.h" |
17 | | |
18 | | #include "nsCOMPtr.h" |
19 | | #include "nsGenericHTMLElement.h" |
20 | | #include "nsGenericHTMLFrameElement.h" |
21 | | #include "nsAttrValueInlines.h" |
22 | | #include "nsIDocShell.h" |
23 | | #include "nsIContentViewer.h" |
24 | | #include "nsIContentInlines.h" |
25 | | #include "nsPresContext.h" |
26 | | #include "nsIPresShell.h" |
27 | | #include "nsIDocument.h" |
28 | | #include "nsView.h" |
29 | | #include "nsViewManager.h" |
30 | | #include "nsGkAtoms.h" |
31 | | #include "nsStyleConsts.h" |
32 | | #include "nsFrameSetFrame.h" |
33 | | #include "nsIScrollable.h" |
34 | | #include "nsNameSpaceManager.h" |
35 | | #include "nsDisplayList.h" |
36 | | #include "nsIScrollableFrame.h" |
37 | | #include "nsIObjectLoadingContent.h" |
38 | | #include "nsLayoutUtils.h" |
39 | | #include "FrameLayerBuilder.h" |
40 | | #include "nsPluginFrame.h" |
41 | | #include "nsContentUtils.h" |
42 | | #include "nsIPermissionManager.h" |
43 | | #include "nsServiceManagerUtils.h" |
44 | | #include "mozilla/Preferences.h" |
45 | | #include "mozilla/dom/HTMLFrameElement.h" |
46 | | #include "RetainedDisplayListBuilder.h" |
47 | | |
48 | | using namespace mozilla; |
49 | | using mozilla::layout::RenderFrameParent; |
50 | | |
51 | | static bool sShowPreviousPage = true; |
52 | | |
53 | | static nsIDocument* |
54 | | GetDocumentFromView(nsView* aView) |
55 | 0 | { |
56 | 0 | MOZ_ASSERT(aView, "null view"); |
57 | 0 |
|
58 | 0 | nsViewManager* vm = aView->GetViewManager(); |
59 | 0 | nsIPresShell* ps = vm ? vm->GetPresShell() : nullptr; |
60 | 0 | return ps ? ps->GetDocument() : nullptr; |
61 | 0 | } |
62 | | |
63 | | nsSubDocumentFrame::nsSubDocumentFrame(ComputedStyle* aStyle) |
64 | | : nsAtomicContainerFrame(aStyle, kClassID) |
65 | | , mOuterView(nullptr) |
66 | | , mInnerView(nullptr) |
67 | | , mIsInline(false) |
68 | | , mPostedReflowCallback(false) |
69 | | , mDidCreateDoc(false) |
70 | | , mCallingShow(false) |
71 | 0 | { |
72 | 0 | } |
73 | | |
74 | | #ifdef ACCESSIBILITY |
75 | | a11y::AccType |
76 | | nsSubDocumentFrame::AccessibleType() |
77 | 0 | { |
78 | 0 | return a11y::eOuterDocType; |
79 | 0 | } |
80 | | #endif |
81 | | |
82 | 0 | NS_QUERYFRAME_HEAD(nsSubDocumentFrame) |
83 | 0 | NS_QUERYFRAME_ENTRY(nsSubDocumentFrame) |
84 | 0 | NS_QUERYFRAME_TAIL_INHERITING(nsAtomicContainerFrame) |
85 | | |
86 | | class AsyncFrameInit : public Runnable |
87 | | { |
88 | | public: |
89 | | explicit AsyncFrameInit(nsIFrame* aFrame) |
90 | | : mozilla::Runnable("AsyncFrameInit") |
91 | | , mFrame(aFrame) |
92 | 0 | { |
93 | 0 | } |
94 | | NS_IMETHOD Run() override |
95 | 0 | { |
96 | 0 | AUTO_PROFILER_LABEL("AsyncFrameInit::Run", OTHER); |
97 | 0 | if (mFrame.IsAlive()) { |
98 | 0 | static_cast<nsSubDocumentFrame*>(mFrame.GetFrame())->ShowViewer(); |
99 | 0 | } |
100 | 0 | return NS_OK; |
101 | 0 | } |
102 | | private: |
103 | | WeakFrame mFrame; |
104 | | }; |
105 | | |
106 | | static void |
107 | | InsertViewsInReverseOrder(nsView* aSibling, nsView* aParent); |
108 | | |
109 | | static void |
110 | | EndSwapDocShellsForViews(nsView* aView); |
111 | | |
112 | | void |
113 | | nsSubDocumentFrame::Init(nsIContent* aContent, |
114 | | nsContainerFrame* aParent, |
115 | | nsIFrame* aPrevInFlow) |
116 | 0 | { |
117 | 0 | MOZ_ASSERT(aContent); |
118 | 0 | // determine if we are a <frame> or <iframe> |
119 | 0 | mIsInline = !aContent->IsHTMLElement(nsGkAtoms::frame); |
120 | 0 |
|
121 | 0 | static bool addedShowPreviousPage = false; |
122 | 0 | if (!addedShowPreviousPage) { |
123 | 0 | // If layout.show_previous_page is true then during loading of a new page we |
124 | 0 | // will draw the previous page if the new page has painting suppressed. |
125 | 0 | Preferences::AddBoolVarCache(&sShowPreviousPage, "layout.show_previous_page", true); |
126 | 0 | addedShowPreviousPage = true; |
127 | 0 | } |
128 | 0 |
|
129 | 0 | nsAtomicContainerFrame::Init(aContent, aParent, aPrevInFlow); |
130 | 0 |
|
131 | 0 | // CreateView() creates this frame's view, stored in mOuterView. It needs to |
132 | 0 | // be created first since it's the parent of the inner view, stored in |
133 | 0 | // mInnerView. |
134 | 0 | CreateView(); |
135 | 0 | EnsureInnerView(); |
136 | 0 |
|
137 | 0 | // Set the primary frame now so that nsDocumentViewer::FindContainerView |
138 | 0 | // called from within EndSwapDocShellsForViews below can find it if needed. |
139 | 0 | aContent->SetPrimaryFrame(this); |
140 | 0 |
|
141 | 0 | // If we have a detached subdoc's root view on our frame loader, re-insert |
142 | 0 | // it into the view tree. This happens when we've been reframed, and |
143 | 0 | // ensures the presentation persists across reframes. If the frame element |
144 | 0 | // has changed documents however, we blow away the presentation. |
145 | 0 | RefPtr<nsFrameLoader> frameloader = FrameLoader(); |
146 | 0 | if (frameloader) { |
147 | 0 | nsCOMPtr<nsIDocument> oldContainerDoc; |
148 | 0 | nsIFrame* detachedFrame = |
149 | 0 | frameloader->GetDetachedSubdocFrame(getter_AddRefs(oldContainerDoc)); |
150 | 0 | frameloader->SetDetachedSubdocFrame(nullptr, nullptr); |
151 | 0 | MOZ_ASSERT(oldContainerDoc || !detachedFrame); |
152 | 0 | if (oldContainerDoc) { |
153 | 0 | nsView* detachedView = |
154 | 0 | detachedFrame ? detachedFrame->GetView() : nullptr; |
155 | 0 | if (detachedView && oldContainerDoc == aContent->OwnerDoc()) { |
156 | 0 | // Restore stashed presentation. |
157 | 0 | ::InsertViewsInReverseOrder(detachedView, mInnerView); |
158 | 0 | ::EndSwapDocShellsForViews(mInnerView->GetFirstChild()); |
159 | 0 | } else { |
160 | 0 | // Presentation is for a different document, don't restore it. |
161 | 0 | frameloader->Hide(); |
162 | 0 | } |
163 | 0 | } |
164 | 0 | } |
165 | 0 |
|
166 | 0 | nsContentUtils::AddScriptRunner(new AsyncFrameInit(this)); |
167 | 0 | } |
168 | | |
169 | | void |
170 | | nsSubDocumentFrame::ShowViewer() |
171 | 0 | { |
172 | 0 | if (mCallingShow) { |
173 | 0 | return; |
174 | 0 | } |
175 | 0 | |
176 | 0 | if (!PresContext()->IsDynamic()) { |
177 | 0 | // We let the printing code take care of loading the document; just |
178 | 0 | // create the inner view for it to use. |
179 | 0 | (void) EnsureInnerView(); |
180 | 0 | } else { |
181 | 0 | RefPtr<nsFrameLoader> frameloader = FrameLoader(); |
182 | 0 | if (frameloader) { |
183 | 0 | CSSIntSize margin = GetMarginAttributes(); |
184 | 0 | AutoWeakFrame weakThis(this); |
185 | 0 | mCallingShow = true; |
186 | 0 | const nsAttrValue* attrValue = |
187 | 0 | GetContent()->AsElement()->GetParsedAttr(nsGkAtoms::scrolling); |
188 | 0 | int32_t scrolling = |
189 | 0 | nsGenericHTMLFrameElement::MapScrollingAttribute(attrValue); |
190 | 0 | bool didCreateDoc = |
191 | 0 | frameloader->Show(margin.width, margin.height, |
192 | 0 | scrolling, scrolling, this); |
193 | 0 | if (!weakThis.IsAlive()) { |
194 | 0 | return; |
195 | 0 | } |
196 | 0 | mCallingShow = false; |
197 | 0 | mDidCreateDoc = didCreateDoc; |
198 | 0 | } |
199 | 0 | } |
200 | 0 | } |
201 | | |
202 | | nsIFrame* |
203 | | nsSubDocumentFrame::GetSubdocumentRootFrame() |
204 | 0 | { |
205 | 0 | if (!mInnerView) |
206 | 0 | return nullptr; |
207 | 0 | nsView* subdocView = mInnerView->GetFirstChild(); |
208 | 0 | return subdocView ? subdocView->GetFrame() : nullptr; |
209 | 0 | } |
210 | | |
211 | | nsIPresShell* |
212 | | nsSubDocumentFrame::GetSubdocumentPresShellForPainting(uint32_t aFlags) |
213 | 0 | { |
214 | 0 | if (!mInnerView) |
215 | 0 | return nullptr; |
216 | 0 | |
217 | 0 | nsView* subdocView = mInnerView->GetFirstChild(); |
218 | 0 | if (!subdocView) |
219 | 0 | return nullptr; |
220 | 0 | |
221 | 0 | nsIPresShell* presShell = nullptr; |
222 | 0 |
|
223 | 0 | nsIFrame* subdocRootFrame = subdocView->GetFrame(); |
224 | 0 | if (subdocRootFrame) { |
225 | 0 | presShell = subdocRootFrame->PresShell(); |
226 | 0 | } |
227 | 0 |
|
228 | 0 | // If painting is suppressed in the presshell, we try to look for a better |
229 | 0 | // presshell to use. |
230 | 0 | if (!presShell || (presShell->IsPaintingSuppressed() && |
231 | 0 | !(aFlags & IGNORE_PAINT_SUPPRESSION))) { |
232 | 0 | // During page transition mInnerView will sometimes have two children, the |
233 | 0 | // first being the new page that may not have any frame, and the second |
234 | 0 | // being the old page that will probably have a frame. |
235 | 0 | nsView* nextView = subdocView->GetNextSibling(); |
236 | 0 | nsIFrame* frame = nullptr; |
237 | 0 | if (nextView) { |
238 | 0 | frame = nextView->GetFrame(); |
239 | 0 | } |
240 | 0 | if (frame) { |
241 | 0 | nsIPresShell* ps = frame->PresShell(); |
242 | 0 | if (!presShell || (ps && !ps->IsPaintingSuppressed() && sShowPreviousPage)) { |
243 | 0 | subdocView = nextView; |
244 | 0 | subdocRootFrame = frame; |
245 | 0 | presShell = ps; |
246 | 0 | } |
247 | 0 | } |
248 | 0 | if (!presShell) { |
249 | 0 | // If we don't have a frame we use this roundabout way to get the pres shell. |
250 | 0 | if (!mFrameLoader) |
251 | 0 | return nullptr; |
252 | 0 | nsIDocShell* docShell = mFrameLoader->GetDocShell(IgnoreErrors()); |
253 | 0 | if (!docShell) |
254 | 0 | return nullptr; |
255 | 0 | presShell = docShell->GetPresShell(); |
256 | 0 | } |
257 | 0 | } |
258 | 0 |
|
259 | 0 | return presShell; |
260 | 0 | } |
261 | | |
262 | | |
263 | | |
264 | | |
265 | | ScreenIntSize |
266 | | nsSubDocumentFrame::GetSubdocumentSize() |
267 | 0 | { |
268 | 0 | if (GetStateBits() & NS_FRAME_FIRST_REFLOW) { |
269 | 0 | RefPtr<nsFrameLoader> frameloader = FrameLoader(); |
270 | 0 | if (frameloader) { |
271 | 0 | nsCOMPtr<nsIDocument> oldContainerDoc; |
272 | 0 | nsIFrame* detachedFrame = |
273 | 0 | frameloader->GetDetachedSubdocFrame(getter_AddRefs(oldContainerDoc)); |
274 | 0 | nsView* view = detachedFrame ? detachedFrame->GetView() : nullptr; |
275 | 0 | if (view) { |
276 | 0 | nsSize size = view->GetBounds().Size(); |
277 | 0 | nsPresContext* presContext = detachedFrame->PresContext(); |
278 | 0 | return ScreenIntSize(presContext->AppUnitsToDevPixels(size.width), |
279 | 0 | presContext->AppUnitsToDevPixels(size.height)); |
280 | 0 | } |
281 | 0 | } |
282 | 0 | // Pick some default size for now. Using 10x10 because that's what the |
283 | 0 | // code used to do. |
284 | 0 | return ScreenIntSize(10, 10); |
285 | 0 | } else { |
286 | 0 | nsSize docSizeAppUnits; |
287 | 0 | nsPresContext* presContext = PresContext(); |
288 | 0 | if (GetContent()->IsHTMLElement(nsGkAtoms::frame)) { |
289 | 0 | docSizeAppUnits = GetSize(); |
290 | 0 | } else { |
291 | 0 | docSizeAppUnits = GetContentRect().Size(); |
292 | 0 | } |
293 | 0 | // Adjust subdocument size, according to 'object-fit' and the |
294 | 0 | // subdocument's intrinsic size and ratio. |
295 | 0 | nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame(); |
296 | 0 | if (subDocRoot) { |
297 | 0 | nsRect destRect = |
298 | 0 | nsLayoutUtils::ComputeObjectDestRect(nsRect(nsPoint(), docSizeAppUnits), |
299 | 0 | subDocRoot->GetIntrinsicSize(), |
300 | 0 | subDocRoot->GetIntrinsicRatio(), |
301 | 0 | StylePosition()); |
302 | 0 | docSizeAppUnits = destRect.Size(); |
303 | 0 | } |
304 | 0 |
|
305 | 0 | return ScreenIntSize(presContext->AppUnitsToDevPixels(docSizeAppUnits.width), |
306 | 0 | presContext->AppUnitsToDevPixels(docSizeAppUnits.height)); |
307 | 0 | } |
308 | 0 | } |
309 | | |
310 | | static void |
311 | | WrapBackgroundColorInOwnLayer(nsDisplayListBuilder* aBuilder, |
312 | | nsIFrame* aFrame, |
313 | | nsDisplayList* aList) |
314 | 0 | { |
315 | 0 | nsDisplayList tempItems; |
316 | 0 | nsDisplayItem* item; |
317 | 0 | while ((item = aList->RemoveBottom()) != nullptr) { |
318 | 0 | if (item->GetType() == DisplayItemType::TYPE_BACKGROUND_COLOR) { |
319 | 0 | nsDisplayList tmpList; |
320 | 0 | tmpList.AppendToTop(item); |
321 | 0 | item = MakeDisplayItem<nsDisplayOwnLayer>(aBuilder, aFrame, &tmpList, aBuilder->CurrentActiveScrolledRoot()); |
322 | 0 | } |
323 | 0 | tempItems.AppendToTop(item); |
324 | 0 | } |
325 | 0 | aList->AppendToTop(&tempItems); |
326 | 0 | } |
327 | | |
328 | | void |
329 | | nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
330 | | const nsDisplayListSet& aLists) |
331 | 0 | { |
332 | 0 | if (!IsVisibleForPainting(aBuilder)) |
333 | 0 | return; |
334 | 0 | |
335 | 0 | nsFrameLoader* frameLoader = FrameLoader(); |
336 | 0 | RenderFrameParent* rfp = nullptr; |
337 | 0 | if (frameLoader) { |
338 | 0 | rfp = frameLoader->GetCurrentRenderFrame(); |
339 | 0 | } |
340 | 0 |
|
341 | 0 | // If we are pointer-events:none then we don't need to HitTest background |
342 | 0 | bool pointerEventsNone = |
343 | 0 | StyleUI()->mPointerEvents == NS_STYLE_POINTER_EVENTS_NONE; |
344 | 0 | if (!aBuilder->IsForEventDelivery() || !pointerEventsNone) { |
345 | 0 | nsDisplayListCollection decorations(aBuilder); |
346 | 0 | DisplayBorderBackgroundOutline(aBuilder, decorations); |
347 | 0 | if (rfp) { |
348 | 0 | // Wrap background colors of <iframe>s with remote subdocuments in their |
349 | 0 | // own layer so we generate a ColorLayer. This is helpful for optimizing |
350 | 0 | // compositing; we can skip compositing the ColorLayer when the |
351 | 0 | // remote content is opaque. |
352 | 0 | WrapBackgroundColorInOwnLayer(aBuilder, this, decorations.BorderBackground()); |
353 | 0 | } |
354 | 0 | decorations.MoveTo(aLists); |
355 | 0 | } |
356 | 0 |
|
357 | 0 | if (aBuilder->IsForEventDelivery() && pointerEventsNone) { |
358 | 0 | return; |
359 | 0 | } |
360 | 0 | |
361 | 0 | // If we're passing pointer events to children then we have to descend into |
362 | 0 | // subdocuments no matter what, to determine which parts are transparent for |
363 | 0 | // hit-testing or event regions. |
364 | 0 | bool needToDescend = aBuilder->GetDescendIntoSubdocuments(); |
365 | 0 | if (!mInnerView || !needToDescend) { |
366 | 0 | return; |
367 | 0 | } |
368 | 0 | |
369 | 0 | if (rfp) { |
370 | 0 | rfp->BuildDisplayList(aBuilder, this, aLists); |
371 | 0 | return; |
372 | 0 | } |
373 | 0 | |
374 | 0 | nsCOMPtr<nsIPresShell> presShell = |
375 | 0 | GetSubdocumentPresShellForPainting( |
376 | 0 | aBuilder->IsIgnoringPaintSuppression() ? IGNORE_PAINT_SUPPRESSION : 0); |
377 | 0 |
|
378 | 0 | if (!presShell) { |
379 | 0 | return; |
380 | 0 | } |
381 | 0 | |
382 | 0 | if (aBuilder->IsInFilter()) { |
383 | 0 | nsIDocument* outerDoc = PresShell()->GetDocument(); |
384 | 0 | nsIDocument* innerDoc = presShell->GetDocument(); |
385 | 0 | if (outerDoc && innerDoc) { |
386 | 0 | if (!outerDoc->NodePrincipal()->Equals(innerDoc->NodePrincipal())) { |
387 | 0 | outerDoc->SetDocumentAndPageUseCounter(eUseCounter_custom_FilteredCrossOriginIFrame); |
388 | 0 | } |
389 | 0 | } |
390 | 0 | } |
391 | 0 |
|
392 | 0 | nsIFrame* subdocRootFrame = presShell->GetRootFrame(); |
393 | 0 |
|
394 | 0 | nsPresContext* presContext = presShell->GetPresContext(); |
395 | 0 |
|
396 | 0 | int32_t parentAPD = PresContext()->AppUnitsPerDevPixel(); |
397 | 0 | int32_t subdocAPD = presContext->AppUnitsPerDevPixel(); |
398 | 0 |
|
399 | 0 | nsRect visible; |
400 | 0 | nsRect dirty; |
401 | 0 | bool haveDisplayPort = false; |
402 | 0 | bool ignoreViewportScrolling = false; |
403 | 0 | nsIFrame* savedIgnoreScrollFrame = nullptr; |
404 | 0 | if (subdocRootFrame) { |
405 | 0 | // get the dirty rect relative to the root frame of the subdoc |
406 | 0 | visible = aBuilder->GetVisibleRect() + GetOffsetToCrossDoc(subdocRootFrame); |
407 | 0 | dirty = aBuilder->GetDirtyRect() + GetOffsetToCrossDoc(subdocRootFrame); |
408 | 0 | // and convert into the appunits of the subdoc |
409 | 0 | visible = visible.ScaleToOtherAppUnitsRoundOut(parentAPD, subdocAPD); |
410 | 0 | dirty = dirty.ScaleToOtherAppUnitsRoundOut(parentAPD, subdocAPD); |
411 | 0 |
|
412 | 0 | if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) { |
413 | 0 | nsIScrollableFrame* rootScrollableFrame = presShell->GetRootScrollFrameAsScrollable(); |
414 | 0 | MOZ_ASSERT(rootScrollableFrame); |
415 | 0 | // Use a copy, so the rects don't get modified. |
416 | 0 | nsRect copyOfDirty = dirty; |
417 | 0 | nsRect copyOfVisible = visible; |
418 | 0 | haveDisplayPort = rootScrollableFrame->DecideScrollableLayer(aBuilder, |
419 | 0 | ©OfVisible, ©OfDirty, |
420 | 0 | /* aSetBase = */ true); |
421 | 0 |
|
422 | 0 | if (!gfxPrefs::LayoutUseContainersForRootFrames() || |
423 | 0 | !aBuilder->IsPaintingToWindow()) { |
424 | 0 | haveDisplayPort = false; |
425 | 0 | } |
426 | 0 |
|
427 | 0 | ignoreViewportScrolling = presShell->IgnoringViewportScrolling(); |
428 | 0 | if (ignoreViewportScrolling) { |
429 | 0 | savedIgnoreScrollFrame = aBuilder->GetIgnoreScrollFrame(); |
430 | 0 | aBuilder->SetIgnoreScrollFrame(rootScrollFrame); |
431 | 0 | } |
432 | 0 | } |
433 | 0 |
|
434 | 0 | aBuilder->EnterPresShell(subdocRootFrame, pointerEventsNone); |
435 | 0 | aBuilder->IncrementPresShellPaintCount(presShell); |
436 | 0 | } else { |
437 | 0 | visible = aBuilder->GetVisibleRect(); |
438 | 0 | dirty = aBuilder->GetDirtyRect(); |
439 | 0 | } |
440 | 0 |
|
441 | 0 | DisplayListClipState::AutoSaveRestore clipState(aBuilder); |
442 | 0 | if (ShouldClipSubdocument()) { |
443 | 0 | clipState.ClipContainingBlockDescendantsToContentBox(aBuilder, this); |
444 | 0 | } |
445 | 0 |
|
446 | 0 | nsIScrollableFrame *sf = presShell->GetRootScrollFrameAsScrollable(); |
447 | 0 | bool constructResolutionItem = subdocRootFrame && |
448 | 0 | (presShell->GetResolution() != 1.0); |
449 | 0 | bool constructZoomItem = subdocRootFrame && parentAPD != subdocAPD; |
450 | 0 | bool needsOwnLayer = false; |
451 | 0 | if (constructResolutionItem || |
452 | 0 | constructZoomItem || |
453 | 0 | haveDisplayPort || |
454 | 0 | presContext->IsRootContentDocument() || |
455 | 0 | (sf && sf->IsScrollingActive(aBuilder))) |
456 | 0 | { |
457 | 0 | needsOwnLayer = true; |
458 | 0 | } |
459 | 0 |
|
460 | 0 | if (aBuilder->IsRetainingDisplayList()) { |
461 | 0 | // Caret frame changed, rebuild the entire subdoc. |
462 | 0 | // We could just invalidate the old and new frame |
463 | 0 | // areas and save some work here. RetainedDisplayListBuilder |
464 | 0 | // does this, so we could teach it to find and check all |
465 | 0 | // subdocs in advance. |
466 | 0 | if (mPreviousCaret != aBuilder->GetCaretFrame()) { |
467 | 0 | dirty = visible; |
468 | 0 | aBuilder->RebuildAllItemsInCurrentSubtree(); |
469 | 0 | // Mark the old caret frame as invalid so that we remove the |
470 | 0 | // old nsDisplayCaret. We don't mark the current frame as invalid |
471 | 0 | // since we want the nsDisplaySubdocument to retain it's place |
472 | 0 | // in the retained display list. |
473 | 0 | if (mPreviousCaret) { |
474 | 0 | aBuilder->MarkFrameModifiedDuringBuilding(mPreviousCaret); |
475 | 0 | } |
476 | 0 | if (aBuilder->GetCaretFrame()) { |
477 | 0 | aBuilder->MarkFrameModifiedDuringBuilding(aBuilder->GetCaretFrame()); |
478 | 0 | } |
479 | 0 | } |
480 | 0 | mPreviousCaret = aBuilder->GetCaretFrame(); |
481 | 0 | } |
482 | 0 |
|
483 | 0 | nsDisplayList childItems; |
484 | 0 |
|
485 | 0 | { |
486 | 0 | DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder); |
487 | 0 | if (needsOwnLayer) { |
488 | 0 | // Clear current clip. There's no point in propagating it down, since |
489 | 0 | // the layer we will construct will be clipped by the current clip. |
490 | 0 | // In fact for nsDisplayZoom propagating it down would be incorrect since |
491 | 0 | // nsDisplayZoom changes the meaning of appunits. |
492 | 0 | nestedClipState.Clear(); |
493 | 0 | } |
494 | 0 |
|
495 | 0 | // Invoke AutoBuildingDisplayList to ensure that the correct dirty rect |
496 | 0 | // is used to compute the visible rect if AddCanvasBackgroundColorItem |
497 | 0 | // creates a display item. |
498 | 0 | nsIFrame* frame = subdocRootFrame ? subdocRootFrame : this; |
499 | 0 | nsDisplayListBuilder::AutoBuildingDisplayList |
500 | 0 | building(aBuilder, frame, visible, dirty, true); |
501 | 0 |
|
502 | 0 | if (subdocRootFrame) { |
503 | 0 | nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame(); |
504 | 0 | nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter( |
505 | 0 | aBuilder, |
506 | 0 | ignoreViewportScrolling && rootScrollFrame && rootScrollFrame->GetContent() |
507 | 0 | ? nsLayoutUtils::FindOrCreateIDFor(rootScrollFrame->GetContent()) |
508 | 0 | : aBuilder->GetCurrentScrollParentId()); |
509 | 0 |
|
510 | 0 | bool hasDocumentLevelListenersForApzAwareEvents = |
511 | 0 | gfxPlatform::AsyncPanZoomEnabled() && |
512 | 0 | nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(presShell); |
513 | 0 |
|
514 | 0 | aBuilder->SetAncestorHasApzAwareEventHandler(hasDocumentLevelListenersForApzAwareEvents); |
515 | 0 | subdocRootFrame-> |
516 | 0 | BuildDisplayListForStackingContext(aBuilder, &childItems); |
517 | 0 | } |
518 | 0 |
|
519 | 0 | if (!aBuilder->IsForEventDelivery()) { |
520 | 0 | // If we are going to use a displayzoom below then any items we put under |
521 | 0 | // it need to have underlying frames from the subdocument. So we need to |
522 | 0 | // calculate the bounds based on which frame will be the underlying frame |
523 | 0 | // for the canvas background color item. |
524 | 0 | nsRect bounds = GetContentRectRelativeToSelf() + |
525 | 0 | aBuilder->ToReferenceFrame(this); |
526 | 0 | if (subdocRootFrame) { |
527 | 0 | bounds = bounds.ScaleToOtherAppUnitsRoundOut(parentAPD, subdocAPD); |
528 | 0 | } |
529 | 0 |
|
530 | 0 | // If we are in print preview/page layout we want to paint the grey |
531 | 0 | // background behind the page, not the canvas color. The canvas color gets |
532 | 0 | // painted on the page itself. |
533 | 0 | if (nsLayoutUtils::NeedsPrintPreviewBackground(presContext)) { |
534 | 0 | presShell->AddPrintPreviewBackgroundItem( |
535 | 0 | *aBuilder, childItems, frame, bounds); |
536 | 0 | } else { |
537 | 0 | // Add the canvas background color to the bottom of the list. This |
538 | 0 | // happens after we've built the list so that AddCanvasBackgroundColorItem |
539 | 0 | // can monkey with the contents if necessary. |
540 | 0 | uint32_t flags = nsIPresShell::FORCE_DRAW | nsIPresShell::ADD_FOR_SUBDOC; |
541 | 0 | presShell->AddCanvasBackgroundColorItem( |
542 | 0 | *aBuilder, childItems, frame, bounds, NS_RGBA(0,0,0,0), flags); |
543 | 0 | } |
544 | 0 | } |
545 | 0 | } |
546 | 0 |
|
547 | 0 | if (subdocRootFrame) { |
548 | 0 | aBuilder->LeavePresShell(subdocRootFrame, &childItems); |
549 | 0 |
|
550 | 0 | if (ignoreViewportScrolling) { |
551 | 0 | aBuilder->SetIgnoreScrollFrame(savedIgnoreScrollFrame); |
552 | 0 | } |
553 | 0 | } |
554 | 0 |
|
555 | 0 | // Generate a resolution and/or zoom item if needed. If one or both of those is |
556 | 0 | // created, we don't need to create a separate nsDisplaySubDocument. |
557 | 0 |
|
558 | 0 | nsDisplayOwnLayerFlags flags = nsDisplayOwnLayerFlags::eGenerateSubdocInvalidations; |
559 | 0 | // If ignoreViewportScrolling is true then the top most layer we create here |
560 | 0 | // is going to become the scrollable layer for the root scroll frame, so we |
561 | 0 | // want to add nsDisplayOwnLayer::GENERATE_SCROLLABLE_LAYER to whatever layer |
562 | 0 | // becomes the topmost. We do this below. |
563 | 0 | if (constructZoomItem) { |
564 | 0 | nsDisplayOwnLayerFlags zoomFlags = flags; |
565 | 0 | if (ignoreViewportScrolling && !constructResolutionItem) { |
566 | 0 | zoomFlags |= nsDisplayOwnLayerFlags::eGenerateScrollableLayer; |
567 | 0 | } |
568 | 0 | nsDisplayZoom* zoomItem = MakeDisplayItem<nsDisplayZoom>( |
569 | 0 | aBuilder, subdocRootFrame, this, &childItems, subdocAPD, parentAPD, zoomFlags); |
570 | 0 |
|
571 | 0 | childItems.AppendToTop(zoomItem); |
572 | 0 | needsOwnLayer = false; |
573 | 0 | } |
574 | 0 | // Wrap the zoom item in the resolution item if we have both because we want the |
575 | 0 | // resolution scale applied on top of the app units per dev pixel conversion. |
576 | 0 | if (ignoreViewportScrolling) { |
577 | 0 | flags |= nsDisplayOwnLayerFlags::eGenerateScrollableLayer; |
578 | 0 | } |
579 | 0 | if (constructResolutionItem) { |
580 | 0 | nsDisplayResolution* resolutionItem = MakeDisplayItem<nsDisplayResolution>( |
581 | 0 | aBuilder, subdocRootFrame, this, &childItems, flags); |
582 | 0 |
|
583 | 0 | childItems.AppendToTop(resolutionItem); |
584 | 0 | needsOwnLayer = false; |
585 | 0 | } |
586 | 0 |
|
587 | 0 | // We always want top level content documents to be in their own layer. |
588 | 0 | nsDisplaySubDocument* layerItem = MakeDisplayItem<nsDisplaySubDocument>( |
589 | 0 | aBuilder, subdocRootFrame ? subdocRootFrame : this, this, |
590 | 0 | &childItems, flags); |
591 | 0 | childItems.AppendToTop(layerItem); |
592 | 0 | layerItem->SetShouldFlattenAway(!needsOwnLayer); |
593 | 0 |
|
594 | 0 | // If we're using containers for root frames, then the earlier call |
595 | 0 | // to AddCanvasBackgroundColorItem won't have been able to add an |
596 | 0 | // unscrolled color item for overscroll. Try again now that we're |
597 | 0 | // outside the scrolled ContainerLayer. |
598 | 0 | if (!aBuilder->IsForEventDelivery() && |
599 | 0 | gfxPrefs::LayoutUseContainersForRootFrames() && |
600 | 0 | !nsLayoutUtils::NeedsPrintPreviewBackground(presContext)) { |
601 | 0 | nsRect bounds = GetContentRectRelativeToSelf() + |
602 | 0 | aBuilder->ToReferenceFrame(this); |
603 | 0 |
|
604 | 0 | // Invoke AutoBuildingDisplayList to ensure that the correct dirty rect |
605 | 0 | // is used to compute the visible rect if AddCanvasBackgroundColorItem |
606 | 0 | // creates a display item. |
607 | 0 | nsDisplayListBuilder::AutoBuildingDisplayList |
608 | 0 | building(aBuilder, this, visible, dirty, true); |
609 | 0 | // Add the canvas background color to the bottom of the list. This |
610 | 0 | // happens after we've built the list so that AddCanvasBackgroundColorItem |
611 | 0 | // can monkey with the contents if necessary. |
612 | 0 | uint32_t flags = nsIPresShell::FORCE_DRAW | nsIPresShell::APPEND_UNSCROLLED_ONLY; |
613 | 0 | presShell->AddCanvasBackgroundColorItem( |
614 | 0 | *aBuilder, childItems, this, bounds, NS_RGBA(0,0,0,0), flags); |
615 | 0 | } |
616 | 0 |
|
617 | 0 | if (aBuilder->IsForFrameVisibility()) { |
618 | 0 | // We don't add the childItems to the return list as we're dealing with them here. |
619 | 0 | presShell->RebuildApproximateFrameVisibilityDisplayList(childItems); |
620 | 0 | childItems.DeleteAll(aBuilder); |
621 | 0 | } else { |
622 | 0 | aLists.Content()->AppendToTop(&childItems); |
623 | 0 | } |
624 | 0 | } |
625 | | |
626 | | nscoord |
627 | | nsSubDocumentFrame::GetIntrinsicISize() |
628 | 0 | { |
629 | 0 | if (!IsInline()) { |
630 | 0 | return 0; // HTML <frame> has no useful intrinsic isize |
631 | 0 | } |
632 | 0 | |
633 | 0 | if (mContent->IsXULElement()) { |
634 | 0 | return 0; // XUL <iframe> and <browser> have no useful intrinsic isize |
635 | 0 | } |
636 | 0 | |
637 | 0 | NS_ASSERTION(ObtainIntrinsicSizeFrame() == nullptr, |
638 | 0 | "Intrinsic isize should come from the embedded document."); |
639 | 0 |
|
640 | 0 | // We must be an HTML <iframe>. Default to size of 300px x 150px, for IE |
641 | 0 | // compat (and per CSS2.1 draft). |
642 | 0 | WritingMode wm = GetWritingMode(); |
643 | 0 | return nsPresContext::CSSPixelsToAppUnits(wm.IsVertical() ? 150 : 300); |
644 | 0 | } |
645 | | |
646 | | nscoord |
647 | | nsSubDocumentFrame::GetIntrinsicBSize() |
648 | 0 | { |
649 | 0 | // <frame> processing does not use this routine, only <iframe> |
650 | 0 | NS_ASSERTION(IsInline(), "Shouldn't have been called"); |
651 | 0 |
|
652 | 0 | if (mContent->IsXULElement()) { |
653 | 0 | return 0; |
654 | 0 | } |
655 | 0 | |
656 | 0 | NS_ASSERTION(ObtainIntrinsicSizeFrame() == nullptr, |
657 | 0 | "Intrinsic bsize should come from the embedded document."); |
658 | 0 |
|
659 | 0 | // Use size of 300px x 150px, for compatibility with IE, and per CSS2.1 draft. |
660 | 0 | WritingMode wm = GetWritingMode(); |
661 | 0 | return nsPresContext::CSSPixelsToAppUnits(wm.IsVertical() ? 300 : 150); |
662 | 0 | } |
663 | | |
664 | | #ifdef DEBUG_FRAME_DUMP |
665 | | void |
666 | | nsSubDocumentFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const |
667 | | { |
668 | | nsCString str; |
669 | | ListGeneric(str, aPrefix, aFlags); |
670 | | fprintf_stderr(out, "%s\n", str.get()); |
671 | | |
672 | | if (aFlags & TRAVERSE_SUBDOCUMENT_FRAMES) { |
673 | | nsSubDocumentFrame* f = const_cast<nsSubDocumentFrame*>(this); |
674 | | nsIFrame* subdocRootFrame = f->GetSubdocumentRootFrame(); |
675 | | if (subdocRootFrame) { |
676 | | nsCString pfx(aPrefix); |
677 | | pfx += " "; |
678 | | subdocRootFrame->List(out, pfx.get(), aFlags); |
679 | | } |
680 | | } |
681 | | } |
682 | | |
683 | | nsresult nsSubDocumentFrame::GetFrameName(nsAString& aResult) const |
684 | | { |
685 | | return MakeFrameName(NS_LITERAL_STRING("FrameOuter"), aResult); |
686 | | } |
687 | | #endif |
688 | | |
689 | | /* virtual */ nscoord |
690 | | nsSubDocumentFrame::GetMinISize(gfxContext *aRenderingContext) |
691 | 0 | { |
692 | 0 | nscoord result; |
693 | 0 | DISPLAY_MIN_INLINE_SIZE(this, result); |
694 | 0 |
|
695 | 0 | nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame(); |
696 | 0 | if (subDocRoot) { |
697 | 0 | result = subDocRoot->GetMinISize(aRenderingContext); |
698 | 0 | } else { |
699 | 0 | result = GetIntrinsicISize(); |
700 | 0 | } |
701 | 0 |
|
702 | 0 | return result; |
703 | 0 | } |
704 | | |
705 | | /* virtual */ nscoord |
706 | | nsSubDocumentFrame::GetPrefISize(gfxContext *aRenderingContext) |
707 | 0 | { |
708 | 0 | nscoord result; |
709 | 0 | DISPLAY_PREF_INLINE_SIZE(this, result); |
710 | 0 |
|
711 | 0 | nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame(); |
712 | 0 | if (subDocRoot) { |
713 | 0 | result = subDocRoot->GetPrefISize(aRenderingContext); |
714 | 0 | } else { |
715 | 0 | result = GetIntrinsicISize(); |
716 | 0 | } |
717 | 0 |
|
718 | 0 | return result; |
719 | 0 | } |
720 | | |
721 | | /* virtual */ IntrinsicSize |
722 | | nsSubDocumentFrame::GetIntrinsicSize() |
723 | 0 | { |
724 | 0 | nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame(); |
725 | 0 | if (subDocRoot) { |
726 | 0 | return subDocRoot->GetIntrinsicSize(); |
727 | 0 | } |
728 | 0 | return nsAtomicContainerFrame::GetIntrinsicSize(); |
729 | 0 | } |
730 | | |
731 | | /* virtual */ nsSize |
732 | | nsSubDocumentFrame::GetIntrinsicRatio() |
733 | 0 | { |
734 | 0 | nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame(); |
735 | 0 | if (subDocRoot) { |
736 | 0 | return subDocRoot->GetIntrinsicRatio(); |
737 | 0 | } |
738 | 0 | return nsAtomicContainerFrame::GetIntrinsicRatio(); |
739 | 0 | } |
740 | | |
741 | | /* virtual */ |
742 | | LogicalSize |
743 | | nsSubDocumentFrame::ComputeAutoSize(gfxContext* aRenderingContext, |
744 | | WritingMode aWM, |
745 | | const LogicalSize& aCBSize, |
746 | | nscoord aAvailableISize, |
747 | | const LogicalSize& aMargin, |
748 | | const LogicalSize& aBorder, |
749 | | const LogicalSize& aPadding, |
750 | | ComputeSizeFlags aFlags) |
751 | 0 | { |
752 | 0 | if (!IsInline()) { |
753 | 0 | return nsFrame::ComputeAutoSize(aRenderingContext, aWM, aCBSize, |
754 | 0 | aAvailableISize, aMargin, aBorder, |
755 | 0 | aPadding, aFlags); |
756 | 0 | } |
757 | 0 | |
758 | 0 | const WritingMode wm = GetWritingMode(); |
759 | 0 | LogicalSize result(wm, GetIntrinsicISize(), GetIntrinsicBSize()); |
760 | 0 | return result.ConvertTo(aWM, wm); |
761 | 0 | } |
762 | | |
763 | | |
764 | | /* virtual */ |
765 | | LogicalSize |
766 | | nsSubDocumentFrame::ComputeSize(gfxContext* aRenderingContext, |
767 | | WritingMode aWM, |
768 | | const LogicalSize& aCBSize, |
769 | | nscoord aAvailableISize, |
770 | | const LogicalSize& aMargin, |
771 | | const LogicalSize& aBorder, |
772 | | const LogicalSize& aPadding, |
773 | | ComputeSizeFlags aFlags) |
774 | 0 | { |
775 | 0 | nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame(); |
776 | 0 | if (subDocRoot) { |
777 | 0 | return ComputeSizeWithIntrinsicDimensions(aRenderingContext, aWM, |
778 | 0 | subDocRoot->GetIntrinsicSize(), |
779 | 0 | subDocRoot->GetIntrinsicRatio(), |
780 | 0 | aCBSize, aMargin, aBorder, |
781 | 0 | aPadding, aFlags); |
782 | 0 | } |
783 | 0 | return nsAtomicContainerFrame::ComputeSize(aRenderingContext, aWM, |
784 | 0 | aCBSize, aAvailableISize, |
785 | 0 | aMargin, aBorder, aPadding, |
786 | 0 | aFlags); |
787 | 0 | } |
788 | | |
789 | | void |
790 | | nsSubDocumentFrame::Reflow(nsPresContext* aPresContext, |
791 | | ReflowOutput& aDesiredSize, |
792 | | const ReflowInput& aReflowInput, |
793 | | nsReflowStatus& aStatus) |
794 | 0 | { |
795 | 0 | MarkInReflow(); |
796 | 0 | DO_GLOBAL_REFLOW_COUNT("nsSubDocumentFrame"); |
797 | 0 | DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); |
798 | 0 | MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); |
799 | 0 | NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, |
800 | 0 | ("enter nsSubDocumentFrame::Reflow: maxSize=%d,%d", |
801 | 0 | aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight())); |
802 | 0 |
|
803 | 0 | NS_ASSERTION(aReflowInput.ComputedWidth() != NS_UNCONSTRAINEDSIZE, |
804 | 0 | "Shouldn't have unconstrained stuff here " |
805 | 0 | "thanks to the rules of reflow"); |
806 | 0 | NS_ASSERTION(NS_INTRINSICSIZE != aReflowInput.ComputedHeight(), |
807 | 0 | "Shouldn't have unconstrained stuff here " |
808 | 0 | "thanks to ComputeAutoSize"); |
809 | 0 |
|
810 | 0 | NS_ASSERTION(mContent->GetPrimaryFrame() == this, |
811 | 0 | "Shouldn't happen"); |
812 | 0 |
|
813 | 0 | // XUL <iframe> or <browser>, or HTML <iframe>, <object> or <embed> |
814 | 0 | aDesiredSize.SetSize(aReflowInput.GetWritingMode(), |
815 | 0 | aReflowInput.ComputedSizeWithBorderPadding()); |
816 | 0 |
|
817 | 0 | // "offset" is the offset of our content area from our frame's |
818 | 0 | // top-left corner. |
819 | 0 | nsPoint offset = nsPoint(aReflowInput.ComputedPhysicalBorderPadding().left, |
820 | 0 | aReflowInput.ComputedPhysicalBorderPadding().top); |
821 | 0 |
|
822 | 0 | if (mInnerView) { |
823 | 0 | const nsMargin& bp = aReflowInput.ComputedPhysicalBorderPadding(); |
824 | 0 | nsSize innerSize(aDesiredSize.Width() - bp.LeftRight(), |
825 | 0 | aDesiredSize.Height() - bp.TopBottom()); |
826 | 0 |
|
827 | 0 | // Size & position the view according to 'object-fit' & 'object-position'. |
828 | 0 | nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame(); |
829 | 0 | IntrinsicSize intrinsSize; |
830 | 0 | nsSize intrinsRatio; |
831 | 0 | if (subDocRoot) { |
832 | 0 | intrinsSize = subDocRoot->GetIntrinsicSize(); |
833 | 0 | intrinsRatio = subDocRoot->GetIntrinsicRatio(); |
834 | 0 | } |
835 | 0 | nsRect destRect = |
836 | 0 | nsLayoutUtils::ComputeObjectDestRect(nsRect(offset, innerSize), |
837 | 0 | intrinsSize, intrinsRatio, |
838 | 0 | StylePosition()); |
839 | 0 |
|
840 | 0 | nsViewManager* vm = mInnerView->GetViewManager(); |
841 | 0 | vm->MoveViewTo(mInnerView, destRect.x, destRect.y); |
842 | 0 | vm->ResizeView(mInnerView, nsRect(nsPoint(0, 0), destRect.Size()), true); |
843 | 0 | } |
844 | 0 |
|
845 | 0 | aDesiredSize.SetOverflowAreasToDesiredBounds(); |
846 | 0 | if (!ShouldClipSubdocument()) { |
847 | 0 | nsIFrame* subdocRootFrame = GetSubdocumentRootFrame(); |
848 | 0 | if (subdocRootFrame) { |
849 | 0 | aDesiredSize.mOverflowAreas.UnionWith(subdocRootFrame->GetOverflowAreas() + offset); |
850 | 0 | } |
851 | 0 | } |
852 | 0 |
|
853 | 0 | FinishAndStoreOverflow(&aDesiredSize); |
854 | 0 |
|
855 | 0 | if (!aPresContext->IsPaginated() && !mPostedReflowCallback) { |
856 | 0 | PresShell()->PostReflowCallback(this); |
857 | 0 | mPostedReflowCallback = true; |
858 | 0 | } |
859 | 0 |
|
860 | 0 | NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, |
861 | 0 | ("exit nsSubDocumentFrame::Reflow: size=%d,%d status=%s", |
862 | 0 | aDesiredSize.Width(), aDesiredSize.Height(), ToString(aStatus).c_str())); |
863 | 0 |
|
864 | 0 | NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize); |
865 | 0 | } |
866 | | |
867 | | bool |
868 | | nsSubDocumentFrame::ReflowFinished() |
869 | 0 | { |
870 | 0 | if (mFrameLoader) { |
871 | 0 | AutoWeakFrame weakFrame(this); |
872 | 0 |
|
873 | 0 | mFrameLoader->UpdatePositionAndSize(this); |
874 | 0 |
|
875 | 0 | if (weakFrame.IsAlive()) { |
876 | 0 | // Make sure that we can post a reflow callback in the future. |
877 | 0 | mPostedReflowCallback = false; |
878 | 0 | } |
879 | 0 | } else { |
880 | 0 | mPostedReflowCallback = false; |
881 | 0 | } |
882 | 0 | return false; |
883 | 0 | } |
884 | | |
885 | | void |
886 | | nsSubDocumentFrame::ReflowCallbackCanceled() |
887 | 0 | { |
888 | 0 | mPostedReflowCallback = false; |
889 | 0 | } |
890 | | |
891 | | nsresult |
892 | | nsSubDocumentFrame::AttributeChanged(int32_t aNameSpaceID, |
893 | | nsAtom* aAttribute, |
894 | | int32_t aModType) |
895 | 0 | { |
896 | 0 | if (aNameSpaceID != kNameSpaceID_None) { |
897 | 0 | return NS_OK; |
898 | 0 | } |
899 | 0 | |
900 | 0 | // If the noResize attribute changes, dis/allow frame to be resized |
901 | 0 | if (aAttribute == nsGkAtoms::noresize) { |
902 | 0 | // Note that we're not doing content type checks, but that's ok -- if |
903 | 0 | // they'd fail we will just end up with a null framesetFrame. |
904 | 0 | if (mContent->GetParent()->IsHTMLElement(nsGkAtoms::frameset)) { |
905 | 0 | nsIFrame* parentFrame = GetParent(); |
906 | 0 |
|
907 | 0 | if (parentFrame) { |
908 | 0 | // There is no interface for nsHTMLFramesetFrame so QI'ing to |
909 | 0 | // concrete class, yay! |
910 | 0 | nsHTMLFramesetFrame* framesetFrame = do_QueryFrame(parentFrame); |
911 | 0 | if (framesetFrame) { |
912 | 0 | framesetFrame->RecalculateBorderResize(); |
913 | 0 | } |
914 | 0 | } |
915 | 0 | } |
916 | 0 | } |
917 | 0 | else if (aAttribute == nsGkAtoms::showresizer) { |
918 | 0 | nsIFrame* rootFrame = GetSubdocumentRootFrame(); |
919 | 0 | if (rootFrame) { |
920 | 0 | rootFrame->PresShell()-> |
921 | 0 | FrameNeedsReflow(rootFrame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY); |
922 | 0 | } |
923 | 0 | } |
924 | 0 | else if (aAttribute == nsGkAtoms::marginwidth || |
925 | 0 | aAttribute == nsGkAtoms::marginheight) { |
926 | 0 |
|
927 | 0 | // Retrieve the attributes |
928 | 0 | CSSIntSize margins = GetMarginAttributes(); |
929 | 0 |
|
930 | 0 | // Notify the frameloader |
931 | 0 | RefPtr<nsFrameLoader> frameloader = FrameLoader(); |
932 | 0 | if (frameloader) |
933 | 0 | frameloader->MarginsChanged(margins.width, margins.height); |
934 | 0 | } |
935 | 0 |
|
936 | 0 | return NS_OK; |
937 | 0 | } |
938 | | |
939 | | nsIFrame* |
940 | | NS_NewSubDocumentFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle) |
941 | 0 | { |
942 | 0 | return new (aPresShell) nsSubDocumentFrame(aStyle); |
943 | 0 | } |
944 | | |
945 | | NS_IMPL_FRAMEARENA_HELPERS(nsSubDocumentFrame) |
946 | | |
947 | | class nsHideViewer : public Runnable { |
948 | | public: |
949 | | nsHideViewer(nsIContent* aFrameElement, |
950 | | nsFrameLoader* aFrameLoader, |
951 | | nsIPresShell* aPresShell, |
952 | | bool aHideViewerIfFrameless) |
953 | | : mozilla::Runnable("nsHideViewer") |
954 | | , mFrameElement(aFrameElement) |
955 | | , mFrameLoader(aFrameLoader) |
956 | | , mPresShell(aPresShell) |
957 | | , mHideViewerIfFrameless(aHideViewerIfFrameless) |
958 | 0 | { |
959 | 0 | NS_ASSERTION(mFrameElement, "Must have a frame element"); |
960 | 0 | NS_ASSERTION(mFrameLoader, "Must have a frame loader"); |
961 | 0 | NS_ASSERTION(mPresShell, "Must have a presshell"); |
962 | 0 | } |
963 | | |
964 | | NS_IMETHOD Run() override |
965 | 0 | { |
966 | 0 | // Flush frames, to ensure any pending display:none changes are made. |
967 | 0 | // Note it can be unsafe to flush if we've destroyed the presentation |
968 | 0 | // for some other reason, like if we're shutting down. |
969 | 0 | // |
970 | 0 | // But avoid the flush if we know for sure we're away, like when we're out |
971 | 0 | // of the document already. |
972 | 0 | // |
973 | 0 | // FIXME(emilio): This could still be a perf footgun when removing lots of |
974 | 0 | // siblings where each of them cause the reframe of an ancestor which happen |
975 | 0 | // to contain a subdocument. |
976 | 0 | // |
977 | 0 | // We should find some way to avoid that! |
978 | 0 | if (!mPresShell->IsDestroying() && mFrameElement->IsInComposedDoc()) { |
979 | 0 | mPresShell->FlushPendingNotifications(FlushType::Frames); |
980 | 0 | } |
981 | 0 |
|
982 | 0 | // Either the frame has been constructed by now, or it never will be, |
983 | 0 | // either way we want to clear the stashed views. |
984 | 0 | mFrameLoader->SetDetachedSubdocFrame(nullptr, nullptr); |
985 | 0 |
|
986 | 0 | nsSubDocumentFrame* frame = do_QueryFrame(mFrameElement->GetPrimaryFrame()); |
987 | 0 | if ((!frame && mHideViewerIfFrameless) || |
988 | 0 | mPresShell->IsDestroying()) { |
989 | 0 | // Either the frame element has no nsIFrame or the presshell is being |
990 | 0 | // destroyed. Hide the nsFrameLoader, which destroys the presentation. |
991 | 0 | mFrameLoader->Hide(); |
992 | 0 | } |
993 | 0 | return NS_OK; |
994 | 0 | } |
995 | | private: |
996 | | nsCOMPtr<nsIContent> mFrameElement; |
997 | | RefPtr<nsFrameLoader> mFrameLoader; |
998 | | nsCOMPtr<nsIPresShell> mPresShell; |
999 | | bool mHideViewerIfFrameless; |
1000 | | }; |
1001 | | |
1002 | | static nsView* |
1003 | | BeginSwapDocShellsForViews(nsView* aSibling); |
1004 | | |
1005 | | void |
1006 | | nsSubDocumentFrame::DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData) |
1007 | 0 | { |
1008 | 0 | if (mPostedReflowCallback) { |
1009 | 0 | PresShell()->CancelReflowCallback(this); |
1010 | 0 | mPostedReflowCallback = false; |
1011 | 0 | } |
1012 | 0 |
|
1013 | 0 | // Detach the subdocument's views and stash them in the frame loader. |
1014 | 0 | // We can then reattach them if we're being reframed (for example if |
1015 | 0 | // the frame has been made position:fixed). |
1016 | 0 | RefPtr<nsFrameLoader> frameloader = FrameLoader(); |
1017 | 0 | if (frameloader) { |
1018 | 0 | nsView* detachedViews = ::BeginSwapDocShellsForViews(mInnerView->GetFirstChild()); |
1019 | 0 |
|
1020 | 0 | if (detachedViews && detachedViews->GetFrame()) { |
1021 | 0 | MOZ_ASSERT(mContent->OwnerDoc()); |
1022 | 0 | frameloader->SetDetachedSubdocFrame( |
1023 | 0 | detachedViews->GetFrame(), mContent->OwnerDoc()); |
1024 | 0 |
|
1025 | 0 | // We call nsFrameLoader::HideViewer() in a script runner so that we can |
1026 | 0 | // safely determine whether the frame is being reframed or destroyed. |
1027 | 0 | nsContentUtils::AddScriptRunner( |
1028 | 0 | new nsHideViewer(mContent, |
1029 | 0 | frameloader, |
1030 | 0 | PresShell(), |
1031 | 0 | (mDidCreateDoc || mCallingShow))); |
1032 | 0 | } else { |
1033 | 0 | frameloader->SetDetachedSubdocFrame(nullptr, nullptr); |
1034 | 0 | if (mDidCreateDoc || mCallingShow) { |
1035 | 0 | frameloader->Hide(); |
1036 | 0 | } |
1037 | 0 | } |
1038 | 0 | } |
1039 | 0 |
|
1040 | 0 | nsAtomicContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData); |
1041 | 0 | } |
1042 | | |
1043 | | CSSIntSize |
1044 | | nsSubDocumentFrame::GetMarginAttributes() |
1045 | 0 | { |
1046 | 0 | CSSIntSize result(-1, -1); |
1047 | 0 | nsGenericHTMLElement *content = nsGenericHTMLElement::FromNode(mContent); |
1048 | 0 | if (content) { |
1049 | 0 | const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::marginwidth); |
1050 | 0 | if (attr && attr->Type() == nsAttrValue::eInteger) |
1051 | 0 | result.width = attr->GetIntegerValue(); |
1052 | 0 | attr = content->GetParsedAttr(nsGkAtoms::marginheight); |
1053 | 0 | if (attr && attr->Type() == nsAttrValue::eInteger) |
1054 | 0 | result.height = attr->GetIntegerValue(); |
1055 | 0 | } |
1056 | 0 | return result; |
1057 | 0 | } |
1058 | | |
1059 | | nsFrameLoader* |
1060 | | nsSubDocumentFrame::FrameLoader() const |
1061 | 0 | { |
1062 | 0 | nsIContent* content = GetContent(); |
1063 | 0 | if (!content) |
1064 | 0 | return nullptr; |
1065 | 0 | |
1066 | 0 | if (!mFrameLoader) { |
1067 | 0 | nsCOMPtr<nsIFrameLoaderOwner> loaderOwner = do_QueryInterface(content); |
1068 | 0 | if (loaderOwner) { |
1069 | 0 | mFrameLoader = loaderOwner->GetFrameLoader(); |
1070 | 0 | } |
1071 | 0 | } |
1072 | 0 | return mFrameLoader; |
1073 | 0 | } |
1074 | | |
1075 | | mozilla::layout::RenderFrameParent* |
1076 | | nsSubDocumentFrame::GetRenderFrameParent() const |
1077 | 0 | { |
1078 | 0 | return FrameLoader() ? FrameLoader()->GetCurrentRenderFrame() : nullptr; |
1079 | 0 | } |
1080 | | |
1081 | | // XXX this should be called ObtainDocShell or something like that, |
1082 | | // to indicate that it could have side effects |
1083 | | nsIDocShell* |
1084 | | nsSubDocumentFrame::GetDocShell() |
1085 | 0 | { |
1086 | 0 | // How can FrameLoader() return null??? |
1087 | 0 | if (NS_WARN_IF(!FrameLoader())) { |
1088 | 0 | return nullptr; |
1089 | 0 | } |
1090 | 0 | return mFrameLoader->GetDocShell(IgnoreErrors()); |
1091 | 0 | } |
1092 | | |
1093 | | static void |
1094 | | DestroyDisplayItemDataForFrames(nsIFrame* aFrame) |
1095 | 0 | { |
1096 | 0 | FrameLayerBuilder::DestroyDisplayItemDataFor(aFrame); |
1097 | 0 |
|
1098 | 0 | nsIFrame::ChildListIterator lists(aFrame); |
1099 | 0 | for (; !lists.IsDone(); lists.Next()) { |
1100 | 0 | nsFrameList::Enumerator childFrames(lists.CurrentList()); |
1101 | 0 | for (; !childFrames.AtEnd(); childFrames.Next()) { |
1102 | 0 | DestroyDisplayItemDataForFrames(childFrames.get()); |
1103 | 0 | } |
1104 | 0 | } |
1105 | 0 | } |
1106 | | |
1107 | | static bool |
1108 | | BeginSwapDocShellsForDocument(nsIDocument* aDocument, void*) |
1109 | 0 | { |
1110 | 0 | MOZ_ASSERT(aDocument, "null document"); |
1111 | 0 |
|
1112 | 0 | nsIPresShell* shell = aDocument->GetShell(); |
1113 | 0 | if (shell) { |
1114 | 0 | // Disable painting while the views are detached, see bug 946929. |
1115 | 0 | shell->SetNeverPainting(true); |
1116 | 0 |
|
1117 | 0 | nsIFrame* rootFrame = shell->GetRootFrame(); |
1118 | 0 | if (rootFrame) { |
1119 | 0 | ::DestroyDisplayItemDataForFrames(rootFrame); |
1120 | 0 | } |
1121 | 0 | } |
1122 | 0 | aDocument->EnumerateActivityObservers( |
1123 | 0 | nsPluginFrame::BeginSwapDocShells, nullptr); |
1124 | 0 | aDocument->EnumerateSubDocuments(BeginSwapDocShellsForDocument, nullptr); |
1125 | 0 | return true; |
1126 | 0 | } |
1127 | | |
1128 | | static nsView* |
1129 | | BeginSwapDocShellsForViews(nsView* aSibling) |
1130 | 0 | { |
1131 | 0 | // Collect the removed sibling views in reverse order in 'removedViews'. |
1132 | 0 | nsView* removedViews = nullptr; |
1133 | 0 | while (aSibling) { |
1134 | 0 | nsIDocument* doc = ::GetDocumentFromView(aSibling); |
1135 | 0 | if (doc) { |
1136 | 0 | ::BeginSwapDocShellsForDocument(doc, nullptr); |
1137 | 0 | } |
1138 | 0 | nsView* next = aSibling->GetNextSibling(); |
1139 | 0 | aSibling->GetViewManager()->RemoveChild(aSibling); |
1140 | 0 | aSibling->SetNextSibling(removedViews); |
1141 | 0 | removedViews = aSibling; |
1142 | 0 | aSibling = next; |
1143 | 0 | } |
1144 | 0 | return removedViews; |
1145 | 0 | } |
1146 | | |
1147 | | static void |
1148 | | InsertViewsInReverseOrder(nsView* aSibling, nsView* aParent) |
1149 | 0 | { |
1150 | 0 | MOZ_ASSERT(aParent, "null view"); |
1151 | 0 | MOZ_ASSERT(!aParent->GetFirstChild(), "inserting into non-empty list"); |
1152 | 0 |
|
1153 | 0 | nsViewManager* vm = aParent->GetViewManager(); |
1154 | 0 | while (aSibling) { |
1155 | 0 | nsView* next = aSibling->GetNextSibling(); |
1156 | 0 | aSibling->SetNextSibling(nullptr); |
1157 | 0 | // true means 'after' in document order which is 'before' in view order, |
1158 | 0 | // so this call prepends the child, thus reversing the siblings as we go. |
1159 | 0 | vm->InsertChild(aParent, aSibling, nullptr, true); |
1160 | 0 | aSibling = next; |
1161 | 0 | } |
1162 | 0 | } |
1163 | | |
1164 | | nsresult |
1165 | | nsSubDocumentFrame::BeginSwapDocShells(nsIFrame* aOther) |
1166 | 0 | { |
1167 | 0 | if (!aOther || !aOther->IsSubDocumentFrame()) { |
1168 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1169 | 0 | } |
1170 | 0 | |
1171 | 0 | nsSubDocumentFrame* other = static_cast<nsSubDocumentFrame*>(aOther); |
1172 | 0 | if (!mFrameLoader || !mDidCreateDoc || mCallingShow || |
1173 | 0 | !other->mFrameLoader || !other->mDidCreateDoc) { |
1174 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1175 | 0 | } |
1176 | 0 | |
1177 | 0 | ClearDisplayItems(); |
1178 | 0 | other->ClearDisplayItems(); |
1179 | 0 |
|
1180 | 0 | if (mInnerView && other->mInnerView) { |
1181 | 0 | nsView* ourSubdocViews = mInnerView->GetFirstChild(); |
1182 | 0 | nsView* ourRemovedViews = ::BeginSwapDocShellsForViews(ourSubdocViews); |
1183 | 0 | nsView* otherSubdocViews = other->mInnerView->GetFirstChild(); |
1184 | 0 | nsView* otherRemovedViews = ::BeginSwapDocShellsForViews(otherSubdocViews); |
1185 | 0 |
|
1186 | 0 | ::InsertViewsInReverseOrder(ourRemovedViews, other->mInnerView); |
1187 | 0 | ::InsertViewsInReverseOrder(otherRemovedViews, mInnerView); |
1188 | 0 | } |
1189 | 0 | mFrameLoader.swap(other->mFrameLoader); |
1190 | 0 | return NS_OK; |
1191 | 0 | } |
1192 | | |
1193 | | static bool |
1194 | | EndSwapDocShellsForDocument(nsIDocument* aDocument, void*) |
1195 | 0 | { |
1196 | 0 | MOZ_ASSERT(aDocument, "null document"); |
1197 | 0 |
|
1198 | 0 | // Our docshell and view trees have been updated for the new hierarchy. |
1199 | 0 | // Now also update all nsDeviceContext::mWidget to that of the |
1200 | 0 | // container view in the new hierarchy. |
1201 | 0 | nsCOMPtr<nsIDocShell> ds = aDocument->GetDocShell(); |
1202 | 0 | if (ds) { |
1203 | 0 | nsCOMPtr<nsIContentViewer> cv; |
1204 | 0 | ds->GetContentViewer(getter_AddRefs(cv)); |
1205 | 0 | while (cv) { |
1206 | 0 | RefPtr<nsPresContext> pc; |
1207 | 0 | cv->GetPresContext(getter_AddRefs(pc)); |
1208 | 0 | if (pc && pc->GetPresShell()) { |
1209 | 0 | pc->GetPresShell()->SetNeverPainting(ds->IsInvisible()); |
1210 | 0 | } |
1211 | 0 | nsDeviceContext* dc = pc ? pc->DeviceContext() : nullptr; |
1212 | 0 | if (dc) { |
1213 | 0 | nsView* v = cv->FindContainerView(); |
1214 | 0 | dc->Init(v ? v->GetNearestWidget(nullptr) : nullptr); |
1215 | 0 | } |
1216 | 0 | nsCOMPtr<nsIContentViewer> prev; |
1217 | 0 | cv->GetPreviousViewer(getter_AddRefs(prev)); |
1218 | 0 | cv = prev; |
1219 | 0 | } |
1220 | 0 | } |
1221 | 0 |
|
1222 | 0 | aDocument->EnumerateActivityObservers( |
1223 | 0 | nsPluginFrame::EndSwapDocShells, nullptr); |
1224 | 0 | aDocument->EnumerateSubDocuments(EndSwapDocShellsForDocument, nullptr); |
1225 | 0 | return true; |
1226 | 0 | } |
1227 | | |
1228 | | static void |
1229 | | EndSwapDocShellsForViews(nsView* aSibling) |
1230 | 0 | { |
1231 | 0 | for ( ; aSibling; aSibling = aSibling->GetNextSibling()) { |
1232 | 0 | nsIDocument* doc = ::GetDocumentFromView(aSibling); |
1233 | 0 | if (doc) { |
1234 | 0 | ::EndSwapDocShellsForDocument(doc, nullptr); |
1235 | 0 | } |
1236 | 0 | nsIFrame *frame = aSibling->GetFrame(); |
1237 | 0 | if (frame) { |
1238 | 0 | nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(frame); |
1239 | 0 | if (parent->HasAnyStateBits(NS_FRAME_IN_POPUP)) { |
1240 | 0 | nsIFrame::AddInPopupStateBitToDescendants(frame); |
1241 | 0 | } else { |
1242 | 0 | nsIFrame::RemoveInPopupStateBitFromDescendants(frame); |
1243 | 0 | } |
1244 | 0 | if (frame->HasInvalidFrameInSubtree()) { |
1245 | 0 | while (parent && !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT | NS_FRAME_IS_NONDISPLAY)) { |
1246 | 0 | parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT); |
1247 | 0 | parent = nsLayoutUtils::GetCrossDocParentFrame(parent); |
1248 | 0 | } |
1249 | 0 | } |
1250 | 0 | } |
1251 | 0 | } |
1252 | 0 | } |
1253 | | |
1254 | | void |
1255 | | nsSubDocumentFrame::EndSwapDocShells(nsIFrame* aOther) |
1256 | 0 | { |
1257 | 0 | nsSubDocumentFrame* other = static_cast<nsSubDocumentFrame*>(aOther); |
1258 | 0 | AutoWeakFrame weakThis(this); |
1259 | 0 | AutoWeakFrame weakOther(aOther); |
1260 | 0 |
|
1261 | 0 | if (mInnerView) { |
1262 | 0 | ::EndSwapDocShellsForViews(mInnerView->GetFirstChild()); |
1263 | 0 | } |
1264 | 0 | if (other->mInnerView) { |
1265 | 0 | ::EndSwapDocShellsForViews(other->mInnerView->GetFirstChild()); |
1266 | 0 | } |
1267 | 0 |
|
1268 | 0 | // Now make sure we reflow both frames, in case their contents |
1269 | 0 | // determine their size. |
1270 | 0 | // And repaint them, for good measure, in case there's nothing |
1271 | 0 | // interesting that happens during reflow. |
1272 | 0 | if (weakThis.IsAlive()) { |
1273 | 0 | PresShell()-> |
1274 | 0 | FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY); |
1275 | 0 | InvalidateFrameSubtree(); |
1276 | 0 | } |
1277 | 0 | if (weakOther.IsAlive()) { |
1278 | 0 | other->PresShell()-> |
1279 | 0 | FrameNeedsReflow(other, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY); |
1280 | 0 | other->InvalidateFrameSubtree(); |
1281 | 0 | } |
1282 | 0 | } |
1283 | | |
1284 | | void |
1285 | | nsSubDocumentFrame::ClearDisplayItems() |
1286 | 0 | { |
1287 | 0 | DisplayItemArray* items = GetProperty(DisplayItems()); |
1288 | 0 | if (!items) { |
1289 | 0 | return; |
1290 | 0 | } |
1291 | 0 | |
1292 | 0 | nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this); |
1293 | 0 | MOZ_ASSERT(displayRoot); |
1294 | 0 |
|
1295 | 0 | RetainedDisplayListBuilder* retainedBuilder = |
1296 | 0 | displayRoot->GetProperty(RetainedDisplayListBuilder::Cached()); |
1297 | 0 | MOZ_ASSERT(retainedBuilder); |
1298 | 0 |
|
1299 | 0 | for (nsDisplayItem* i : *items) { |
1300 | 0 | if (i->GetType() == DisplayItemType::TYPE_SUBDOCUMENT) { |
1301 | 0 | i->GetChildren()->DeleteAll(retainedBuilder->Builder()); |
1302 | 0 | static_cast<nsDisplaySubDocument*>(i)->Disown(); |
1303 | 0 | break; |
1304 | 0 | } |
1305 | 0 | } |
1306 | 0 | } |
1307 | | |
1308 | | nsView* |
1309 | | nsSubDocumentFrame::EnsureInnerView() |
1310 | 0 | { |
1311 | 0 | if (mInnerView) { |
1312 | 0 | return mInnerView; |
1313 | 0 | } |
1314 | 0 | |
1315 | 0 | // create, init, set the parent of the view |
1316 | 0 | nsView* outerView = GetView(); |
1317 | 0 | NS_ASSERTION(outerView, "Must have an outer view already"); |
1318 | 0 | nsRect viewBounds(0, 0, 0, 0); // size will be fixed during reflow |
1319 | 0 |
|
1320 | 0 | nsViewManager* viewMan = outerView->GetViewManager(); |
1321 | 0 | nsView* innerView = viewMan->CreateView(viewBounds, outerView); |
1322 | 0 | if (!innerView) { |
1323 | 0 | NS_ERROR("Could not create inner view"); |
1324 | 0 | return nullptr; |
1325 | 0 | } |
1326 | 0 | mInnerView = innerView; |
1327 | 0 | viewMan->InsertChild(outerView, innerView, nullptr, true); |
1328 | 0 |
|
1329 | 0 | return mInnerView; |
1330 | 0 | } |
1331 | | |
1332 | | nsIFrame* |
1333 | | nsSubDocumentFrame::ObtainIntrinsicSizeFrame() |
1334 | 0 | { |
1335 | 0 | nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(GetContent()); |
1336 | 0 | if (olc) { |
1337 | 0 | // We are an HTML <object> or <embed> (a replaced element). |
1338 | 0 |
|
1339 | 0 | // Try to get an nsIFrame for our sub-document's document element |
1340 | 0 | nsIFrame* subDocRoot = nullptr; |
1341 | 0 |
|
1342 | 0 | nsIDocShell* docShell = GetDocShell(); |
1343 | 0 | if (docShell) { |
1344 | 0 | nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell(); |
1345 | 0 | if (presShell) { |
1346 | 0 | nsIScrollableFrame* scrollable = presShell->GetRootScrollFrameAsScrollable(); |
1347 | 0 | if (scrollable) { |
1348 | 0 | nsIFrame* scrolled = scrollable->GetScrolledFrame(); |
1349 | 0 | if (scrolled) { |
1350 | 0 | subDocRoot = scrolled->PrincipalChildList().FirstChild(); |
1351 | 0 | } |
1352 | 0 | } |
1353 | 0 | } |
1354 | 0 | } |
1355 | 0 |
|
1356 | 0 | if (subDocRoot && subDocRoot->GetContent() && |
1357 | 0 | subDocRoot->GetContent()->NodeInfo()->Equals(nsGkAtoms::svg, kNameSpaceID_SVG)) { |
1358 | 0 | return subDocRoot; // SVG documents have an intrinsic size |
1359 | 0 | } |
1360 | 0 | } |
1361 | 0 | return nullptr; |
1362 | 0 | } |