1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Travis Bogard <travis@netscape.com>
24 : * HÂkan Waara <hwaara@chello.se>
25 : * Mats Palmgren <matspal@gmail.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : /*
42 : * rendering object for replaced elements that contain a document, such
43 : * as <frame>, <iframe>, and some <object>s
44 : */
45 :
46 : #include "mozilla/layout/RenderFrameParent.h"
47 :
48 : #include "nsSubDocumentFrame.h"
49 : #include "nsCOMPtr.h"
50 : #include "nsGenericHTMLElement.h"
51 : #include "nsIDocShell.h"
52 : #include "nsIDocShellLoadInfo.h"
53 : #include "nsIDocShellTreeItem.h"
54 : #include "nsIDocShellTreeNode.h"
55 : #include "nsIDocShellTreeOwner.h"
56 : #include "nsIBaseWindow.h"
57 : #include "nsIContentViewer.h"
58 : #include "nsPresContext.h"
59 : #include "nsIPresShell.h"
60 : #include "nsIComponentManager.h"
61 : #include "nsFrameManager.h"
62 : #include "nsIStreamListener.h"
63 : #include "nsIURL.h"
64 : #include "nsNetUtil.h"
65 : #include "nsIDocument.h"
66 : #include "nsIView.h"
67 : #include "nsIViewManager.h"
68 : #include "nsGkAtoms.h"
69 : #include "nsStyleCoord.h"
70 : #include "nsStyleContext.h"
71 : #include "nsStyleConsts.h"
72 : #include "nsFrameSetFrame.h"
73 : #include "nsIDOMHTMLFrameElement.h"
74 : #include "nsIDOMHTMLIFrameElement.h"
75 : #include "nsIDOMXULElement.h"
76 : #include "nsIScriptSecurityManager.h"
77 : #include "nsXPIDLString.h"
78 : #include "nsIScrollable.h"
79 : #include "nsINameSpaceManager.h"
80 : #include "nsWeakReference.h"
81 : #include "nsIDOMWindow.h"
82 : #include "nsIDOMDocument.h"
83 : #include "nsDisplayList.h"
84 : #include "nsUnicharUtils.h"
85 : #include "nsIScrollableFrame.h"
86 : #include "nsIObjectLoadingContent.h"
87 : #include "nsLayoutUtils.h"
88 : #include "FrameLayerBuilder.h"
89 : #include "nsObjectFrame.h"
90 : #include "nsIServiceManager.h"
91 : #include "nsContentUtils.h"
92 :
93 : #ifdef MOZ_XUL
94 : #include "nsXULPopupManager.h"
95 : #endif
96 :
97 : // For Accessibility
98 : #ifdef ACCESSIBILITY
99 : #include "nsAccessibilityService.h"
100 : #endif
101 :
102 : using namespace mozilla;
103 : using mozilla::layout::RenderFrameParent;
104 :
105 : static nsIDocument*
106 0 : GetDocumentFromView(nsIView* aView)
107 : {
108 0 : NS_PRECONDITION(aView, "");
109 :
110 0 : nsIFrame* f = aView->GetFrame();
111 0 : nsIPresShell* ps = f ? f->PresContext()->PresShell() : nsnull;
112 0 : return ps ? ps->GetDocument() : nsnull;
113 : }
114 :
115 : class AsyncFrameInit;
116 :
117 0 : nsSubDocumentFrame::nsSubDocumentFrame(nsStyleContext* aContext)
118 : : nsLeafFrame(aContext)
119 : , mIsInline(false)
120 : , mPostedReflowCallback(false)
121 : , mDidCreateDoc(false)
122 0 : , mCallingShow(false)
123 : {
124 0 : }
125 :
126 : #ifdef ACCESSIBILITY
127 : already_AddRefed<nsAccessible>
128 0 : nsSubDocumentFrame::CreateAccessible()
129 : {
130 0 : nsAccessibilityService* accService = nsIPresShell::AccService();
131 : return accService ?
132 0 : accService->CreateOuterDocAccessible(mContent, PresContext()->PresShell()) :
133 0 : nsnull;
134 : }
135 : #endif
136 :
137 0 : NS_QUERYFRAME_HEAD(nsSubDocumentFrame)
138 0 : NS_QUERYFRAME_ENTRY(nsSubDocumentFrame)
139 0 : NS_QUERYFRAME_TAIL_INHERITING(nsLeafFrame)
140 :
141 : class AsyncFrameInit : public nsRunnable
142 0 : {
143 : public:
144 0 : AsyncFrameInit(nsIFrame* aFrame) : mFrame(aFrame) {}
145 0 : NS_IMETHOD Run()
146 : {
147 0 : if (mFrame.IsAlive()) {
148 0 : static_cast<nsSubDocumentFrame*>(mFrame.GetFrame())->ShowViewer();
149 : }
150 0 : return NS_OK;
151 : }
152 : private:
153 : nsWeakFrame mFrame;
154 : };
155 :
156 : NS_IMETHODIMP
157 0 : nsSubDocumentFrame::Init(nsIContent* aContent,
158 : nsIFrame* aParent,
159 : nsIFrame* aPrevInFlow)
160 : {
161 : // determine if we are a <frame> or <iframe>
162 0 : if (aContent) {
163 0 : nsCOMPtr<nsIDOMHTMLFrameElement> frameElem = do_QueryInterface(aContent);
164 0 : mIsInline = frameElem ? false : true;
165 : }
166 :
167 0 : nsresult rv = nsLeafFrame::Init(aContent, aParent, aPrevInFlow);
168 0 : if (NS_FAILED(rv))
169 0 : return rv;
170 :
171 : // We are going to create an inner view. If we need a view for the
172 : // OuterFrame but we wait for the normal view creation path in
173 : // nsCSSFrameConstructor, then we will lose because the inner view's
174 : // parent will already have been set to some outer view (e.g., the
175 : // canvas) when it really needs to have this frame's view as its
176 : // parent. So, create this frame's view right away, whether we
177 : // really need it or not, and the inner view will get it as the
178 : // parent.
179 0 : if (!HasView()) {
180 0 : rv = nsContainerFrame::CreateViewForFrame(this, true);
181 0 : NS_ENSURE_SUCCESS(rv, rv);
182 : }
183 0 : EnsureInnerView();
184 :
185 : // Set the primary frame now so that
186 : // DocumentViewerImpl::FindContainerView called by ShowViewer below
187 : // can find it if necessary.
188 0 : aContent->SetPrimaryFrame(this);
189 :
190 0 : nsContentUtils::AddScriptRunner(new AsyncFrameInit(this));
191 0 : return NS_OK;
192 : }
193 :
194 0 : inline PRInt32 ConvertOverflow(PRUint8 aOverflow)
195 : {
196 0 : switch (aOverflow) {
197 : case NS_STYLE_OVERFLOW_VISIBLE:
198 : case NS_STYLE_OVERFLOW_AUTO:
199 0 : return nsIScrollable::Scrollbar_Auto;
200 : case NS_STYLE_OVERFLOW_HIDDEN:
201 : case NS_STYLE_OVERFLOW_CLIP:
202 0 : return nsIScrollable::Scrollbar_Never;
203 : case NS_STYLE_OVERFLOW_SCROLL:
204 0 : return nsIScrollable::Scrollbar_Always;
205 : }
206 0 : NS_NOTREACHED("invalid overflow value passed to ConvertOverflow");
207 0 : return nsIScrollable::Scrollbar_Auto;
208 : }
209 :
210 : void
211 0 : nsSubDocumentFrame::ShowViewer()
212 : {
213 0 : if (mCallingShow) {
214 0 : return;
215 : }
216 :
217 0 : if (!PresContext()->IsDynamic()) {
218 : // We let the printing code take care of loading the document; just
219 : // create the inner view for it to use.
220 0 : (void) EnsureInnerView();
221 : } else {
222 0 : nsRefPtr<nsFrameLoader> frameloader = FrameLoader();
223 0 : if (frameloader) {
224 0 : nsIntSize margin = GetMarginAttributes();
225 0 : const nsStyleDisplay* disp = GetStyleDisplay();
226 0 : nsWeakFrame weakThis(this);
227 0 : mCallingShow = true;
228 : bool didCreateDoc =
229 : frameloader->Show(margin.width, margin.height,
230 : ConvertOverflow(disp->mOverflowX),
231 : ConvertOverflow(disp->mOverflowY),
232 0 : this);
233 0 : if (!weakThis.IsAlive()) {
234 : return;
235 : }
236 0 : mCallingShow = false;
237 0 : mDidCreateDoc = didCreateDoc;
238 : }
239 : }
240 : }
241 :
242 : PRIntn
243 0 : nsSubDocumentFrame::GetSkipSides() const
244 : {
245 0 : return 0;
246 : }
247 :
248 : nsIFrame*
249 0 : nsSubDocumentFrame::GetSubdocumentRootFrame()
250 : {
251 0 : if (!mInnerView)
252 0 : return nsnull;
253 0 : nsIView* subdocView = mInnerView->GetFirstChild();
254 0 : return subdocView ? subdocView->GetFrame() : nsnull;
255 : }
256 :
257 : NS_IMETHODIMP
258 0 : nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
259 : const nsRect& aDirtyRect,
260 : const nsDisplayListSet& aLists)
261 : {
262 0 : if (!IsVisibleForPainting(aBuilder))
263 0 : return NS_OK;
264 :
265 0 : if (aBuilder->IsForEventDelivery() &&
266 0 : GetStyleVisibility()->mPointerEvents == NS_STYLE_POINTER_EVENTS_NONE)
267 0 : return NS_OK;
268 :
269 0 : nsresult rv = DisplayBorderBackgroundOutline(aBuilder, aLists);
270 0 : NS_ENSURE_SUCCESS(rv, rv);
271 :
272 0 : if (!mInnerView)
273 0 : return NS_OK;
274 :
275 0 : nsFrameLoader* frameLoader = FrameLoader();
276 0 : if (frameLoader) {
277 0 : RenderFrameParent* rfp = frameLoader->GetCurrentRemoteFrame();
278 0 : if (rfp) {
279 0 : return rfp->BuildDisplayList(aBuilder, this, aDirtyRect, aLists);
280 : }
281 : }
282 :
283 0 : nsIView* subdocView = mInnerView->GetFirstChild();
284 0 : if (!subdocView)
285 0 : return NS_OK;
286 :
287 0 : nsCOMPtr<nsIPresShell> presShell = nsnull;
288 :
289 0 : nsIFrame* subdocRootFrame = subdocView->GetFrame();
290 0 : if (subdocRootFrame) {
291 0 : presShell = subdocRootFrame->PresContext()->PresShell();
292 : }
293 : // If painting is suppressed in the presshell, we try to look for a better
294 : // presshell to use.
295 0 : if (!presShell || (presShell->IsPaintingSuppressed() &&
296 0 : !aBuilder->IsIgnoringPaintSuppression())) {
297 : // During page transition mInnerView will sometimes have two children, the
298 : // first being the new page that may not have any frame, and the second
299 : // being the old page that will probably have a frame.
300 0 : nsIView* nextView = subdocView->GetNextSibling();
301 0 : nsIFrame* frame = nsnull;
302 0 : if (nextView) {
303 0 : frame = nextView->GetFrame();
304 : }
305 0 : if (frame) {
306 0 : nsIPresShell* ps = frame->PresContext()->PresShell();
307 0 : if (!presShell || (ps && !ps->IsPaintingSuppressed())) {
308 0 : subdocView = nextView;
309 0 : subdocRootFrame = frame;
310 0 : presShell = ps;
311 : }
312 : }
313 0 : if (!presShell) {
314 : // If we don't have a frame we use this roundabout way to get the pres shell.
315 0 : if (!mFrameLoader)
316 0 : return NS_OK;
317 0 : nsCOMPtr<nsIDocShell> docShell;
318 0 : mFrameLoader->GetDocShell(getter_AddRefs(docShell));
319 0 : if (!docShell)
320 0 : return NS_OK;
321 0 : docShell->GetPresShell(getter_AddRefs(presShell));
322 0 : if (!presShell)
323 0 : return NS_OK;
324 : }
325 : }
326 :
327 0 : nsPresContext* presContext = presShell->GetPresContext();
328 :
329 0 : nsDisplayList childItems;
330 :
331 0 : PRInt32 parentAPD = PresContext()->AppUnitsPerDevPixel();
332 0 : PRInt32 subdocAPD = presContext->AppUnitsPerDevPixel();
333 :
334 0 : nsRect dirty;
335 0 : if (subdocRootFrame) {
336 : // get the dirty rect relative to the root frame of the subdoc
337 0 : dirty = aDirtyRect + GetOffsetToCrossDoc(subdocRootFrame);
338 : // and convert into the appunits of the subdoc
339 0 : dirty = dirty.ConvertAppUnitsRoundOut(parentAPD, subdocAPD);
340 :
341 0 : aBuilder->EnterPresShell(subdocRootFrame, dirty);
342 : }
343 :
344 : nsRect subdocBoundsInParentUnits =
345 0 : mInnerView->GetBounds() + GetOffsetToCrossDoc(aBuilder->ReferenceFrame());
346 :
347 0 : if (subdocRootFrame) {
348 : rv = subdocRootFrame->
349 0 : BuildDisplayListForStackingContext(aBuilder, dirty, &childItems);
350 : }
351 :
352 0 : if (!aBuilder->IsForEventDelivery()) {
353 : // If we are going to use a displayzoom below then any items we put under
354 : // it need to have underlying frames from the subdocument. So we need to
355 : // calculate the bounds based on which frame will be the underlying frame
356 : // for the canvas background color item.
357 0 : nsRect bounds;
358 0 : if (subdocRootFrame) {
359 0 : bounds = subdocBoundsInParentUnits.ConvertAppUnitsRoundOut(parentAPD, subdocAPD);
360 : } else {
361 0 : bounds = subdocBoundsInParentUnits;
362 : }
363 :
364 : // If we are in print preview/page layout we want to paint the grey
365 : // background behind the page, not the canvas color. The canvas color gets
366 : // painted on the page itself.
367 0 : if (nsLayoutUtils::NeedsPrintPreviewBackground(presContext)) {
368 0 : rv = presShell->AddPrintPreviewBackgroundItem(
369 : *aBuilder, childItems, subdocRootFrame ? subdocRootFrame : this,
370 0 : bounds);
371 : } else {
372 : // Add the canvas background color to the bottom of the list. This
373 : // happens after we've built the list so that AddCanvasBackgroundColorItem
374 : // can monkey with the contents if necessary.
375 0 : PRUint32 flags = nsIPresShell::FORCE_DRAW;
376 0 : rv = presShell->AddCanvasBackgroundColorItem(
377 : *aBuilder, childItems, subdocRootFrame ? subdocRootFrame : this,
378 0 : bounds, NS_RGBA(0,0,0,0), flags);
379 : }
380 : }
381 :
382 0 : bool addedLayer = false;
383 :
384 0 : if (subdocRootFrame && parentAPD != subdocAPD) {
385 0 : NS_WARN_IF_FALSE(!addedLayer,
386 : "Two container layers have been added. "
387 : "Performance may suffer.");
388 0 : addedLayer = true;
389 :
390 : nsDisplayZoom* zoomItem =
391 : new (aBuilder) nsDisplayZoom(aBuilder, subdocRootFrame, &childItems,
392 0 : subdocAPD, parentAPD);
393 0 : childItems.AppendToTop(zoomItem);
394 : }
395 :
396 0 : if (!addedLayer && presContext->IsRootContentDocument()) {
397 : // We always want top level content documents to be in their own layer.
398 : nsDisplayOwnLayer* layerItem = new (aBuilder) nsDisplayOwnLayer(
399 0 : aBuilder, subdocRootFrame ? subdocRootFrame : this, &childItems);
400 0 : childItems.AppendToTop(layerItem);
401 : }
402 :
403 0 : if (subdocRootFrame) {
404 0 : aBuilder->LeavePresShell(subdocRootFrame, dirty);
405 : }
406 :
407 0 : if (ShouldClipSubdocument()) {
408 : nsDisplayClip* item =
409 : new (aBuilder) nsDisplayClip(aBuilder, this, &childItems,
410 0 : subdocBoundsInParentUnits);
411 : // Clip children to the child root frame's rectangle
412 0 : childItems.AppendToTop(item);
413 : }
414 :
415 0 : if (mIsInline) {
416 0 : WrapReplacedContentForBorderRadius(aBuilder, &childItems, aLists);
417 : } else {
418 0 : aLists.Content()->AppendToTop(&childItems);
419 : }
420 :
421 : // delete childItems in case of OOM
422 0 : childItems.DeleteAll();
423 :
424 0 : return rv;
425 : }
426 :
427 : nscoord
428 0 : nsSubDocumentFrame::GetIntrinsicWidth()
429 : {
430 0 : if (!IsInline()) {
431 0 : return 0; // HTML <frame> has no useful intrinsic width
432 : }
433 :
434 0 : if (mContent->IsXUL()) {
435 0 : return 0; // XUL <iframe> and <browser> have no useful intrinsic width
436 : }
437 :
438 0 : NS_ASSERTION(ObtainIntrinsicSizeFrame() == nsnull,
439 : "Intrinsic width should come from the embedded document.");
440 :
441 : // We must be an HTML <iframe>. Default to a width of 300, for IE
442 : // compat (and per CSS2.1 draft).
443 0 : return nsPresContext::CSSPixelsToAppUnits(300);
444 : }
445 :
446 : nscoord
447 0 : nsSubDocumentFrame::GetIntrinsicHeight()
448 : {
449 : // <frame> processing does not use this routine, only <iframe>
450 0 : NS_ASSERTION(IsInline(), "Shouldn't have been called");
451 :
452 0 : if (mContent->IsXUL()) {
453 0 : return 0;
454 : }
455 :
456 0 : NS_ASSERTION(ObtainIntrinsicSizeFrame() == nsnull,
457 : "Intrinsic height should come from the embedded document.");
458 :
459 : // Use 150px, for compatibility with IE, and per CSS2.1 draft.
460 0 : return nsPresContext::CSSPixelsToAppUnits(150);
461 : }
462 :
463 : #ifdef DEBUG
464 0 : NS_IMETHODIMP nsSubDocumentFrame::GetFrameName(nsAString& aResult) const
465 : {
466 0 : return MakeFrameName(NS_LITERAL_STRING("FrameOuter"), aResult);
467 : }
468 : #endif
469 :
470 : nsIAtom*
471 0 : nsSubDocumentFrame::GetType() const
472 : {
473 0 : return nsGkAtoms::subDocumentFrame;
474 : }
475 :
476 : /* virtual */ nscoord
477 0 : nsSubDocumentFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
478 : {
479 : nscoord result;
480 0 : DISPLAY_MIN_WIDTH(this, result);
481 :
482 0 : nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
483 0 : if (subDocRoot) {
484 0 : result = subDocRoot->GetMinWidth(aRenderingContext);
485 : } else {
486 0 : result = GetIntrinsicWidth();
487 : }
488 :
489 0 : return result;
490 : }
491 :
492 : /* virtual */ nscoord
493 0 : nsSubDocumentFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
494 : {
495 : nscoord result;
496 0 : DISPLAY_PREF_WIDTH(this, result);
497 :
498 0 : nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
499 0 : if (subDocRoot) {
500 0 : result = subDocRoot->GetPrefWidth(aRenderingContext);
501 : } else {
502 0 : result = GetIntrinsicWidth();
503 : }
504 :
505 0 : return result;
506 : }
507 :
508 : /* virtual */ nsIFrame::IntrinsicSize
509 0 : nsSubDocumentFrame::GetIntrinsicSize()
510 : {
511 0 : nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
512 0 : if (subDocRoot) {
513 0 : return subDocRoot->GetIntrinsicSize();
514 : }
515 0 : return nsLeafFrame::GetIntrinsicSize();
516 : }
517 :
518 : /* virtual */ nsSize
519 0 : nsSubDocumentFrame::GetIntrinsicRatio()
520 : {
521 0 : nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
522 0 : if (subDocRoot) {
523 0 : return subDocRoot->GetIntrinsicRatio();
524 : }
525 0 : return nsLeafFrame::GetIntrinsicRatio();
526 : }
527 :
528 : /* virtual */ nsSize
529 0 : nsSubDocumentFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
530 : nsSize aCBSize, nscoord aAvailableWidth,
531 : nsSize aMargin, nsSize aBorder,
532 : nsSize aPadding, bool aShrinkWrap)
533 : {
534 0 : if (!IsInline()) {
535 : return nsFrame::ComputeAutoSize(aRenderingContext, aCBSize,
536 : aAvailableWidth, aMargin, aBorder,
537 0 : aPadding, aShrinkWrap);
538 : }
539 :
540 : return nsLeafFrame::ComputeAutoSize(aRenderingContext, aCBSize,
541 : aAvailableWidth, aMargin, aBorder,
542 0 : aPadding, aShrinkWrap);
543 : }
544 :
545 :
546 : /* virtual */ nsSize
547 0 : nsSubDocumentFrame::ComputeSize(nsRenderingContext *aRenderingContext,
548 : nsSize aCBSize, nscoord aAvailableWidth,
549 : nsSize aMargin, nsSize aBorder, nsSize aPadding,
550 : bool aShrinkWrap)
551 : {
552 0 : nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
553 0 : if (subDocRoot) {
554 : return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(
555 : aRenderingContext, this,
556 0 : subDocRoot->GetIntrinsicSize(),
557 0 : subDocRoot->GetIntrinsicRatio(),
558 0 : aCBSize, aMargin, aBorder, aPadding);
559 : }
560 : return nsLeafFrame::ComputeSize(aRenderingContext, aCBSize, aAvailableWidth,
561 0 : aMargin, aBorder, aPadding, aShrinkWrap);
562 : }
563 :
564 : NS_IMETHODIMP
565 0 : nsSubDocumentFrame::Reflow(nsPresContext* aPresContext,
566 : nsHTMLReflowMetrics& aDesiredSize,
567 : const nsHTMLReflowState& aReflowState,
568 : nsReflowStatus& aStatus)
569 : {
570 0 : DO_GLOBAL_REFLOW_COUNT("nsSubDocumentFrame");
571 0 : DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
572 : // printf("OuterFrame::Reflow %X (%d,%d) \n", this, aReflowState.availableWidth, aReflowState.availableHeight);
573 0 : NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
574 : ("enter nsSubDocumentFrame::Reflow: maxSize=%d,%d",
575 : aReflowState.availableWidth, aReflowState.availableHeight));
576 :
577 0 : aStatus = NS_FRAME_COMPLETE;
578 :
579 0 : NS_ASSERTION(mContent->GetPrimaryFrame() == this,
580 : "Shouldn't happen");
581 :
582 : // "offset" is the offset of our content area from our frame's
583 : // top-left corner.
584 0 : nsPoint offset(0, 0);
585 :
586 0 : if (IsInline()) {
587 : // XUL <iframe> or <browser>, or HTML <iframe>, <object> or <embed>
588 : nsresult rv = nsLeafFrame::DoReflow(aPresContext, aDesiredSize, aReflowState,
589 0 : aStatus);
590 0 : NS_ENSURE_SUCCESS(rv, rv);
591 :
592 : offset = nsPoint(aReflowState.mComputedBorderPadding.left,
593 0 : aReflowState.mComputedBorderPadding.top);
594 : } else {
595 : // HTML <frame>
596 0 : SizeToAvailSize(aReflowState, aDesiredSize);
597 : }
598 :
599 0 : nsSize innerSize(aDesiredSize.width, aDesiredSize.height);
600 0 : if (IsInline()) {
601 0 : innerSize.width -= aReflowState.mComputedBorderPadding.LeftRight();
602 0 : innerSize.height -= aReflowState.mComputedBorderPadding.TopBottom();
603 : }
604 :
605 0 : if (mInnerView) {
606 0 : nsIViewManager* vm = mInnerView->GetViewManager();
607 0 : vm->MoveViewTo(mInnerView, offset.x, offset.y);
608 0 : vm->ResizeView(mInnerView, nsRect(nsPoint(0, 0), innerSize), true);
609 : }
610 :
611 0 : aDesiredSize.SetOverflowAreasToDesiredBounds();
612 0 : if (!ShouldClipSubdocument()) {
613 0 : nsIFrame* subdocRootFrame = GetSubdocumentRootFrame();
614 0 : if (subdocRootFrame) {
615 0 : aDesiredSize.mOverflowAreas.UnionWith(subdocRootFrame->GetOverflowAreas() + offset);
616 : }
617 : }
618 :
619 : // Determine if we need to repaint our border, background or outline
620 0 : CheckInvalidateSizeChange(aDesiredSize);
621 :
622 0 : FinishAndStoreOverflow(&aDesiredSize);
623 :
624 0 : if (!aPresContext->IsPaginated() && !mPostedReflowCallback) {
625 0 : PresContext()->PresShell()->PostReflowCallback(this);
626 0 : mPostedReflowCallback = true;
627 : }
628 :
629 : // printf("OuterFrame::Reflow DONE %X (%d,%d)\n", this,
630 : // aDesiredSize.width, aDesiredSize.height);
631 :
632 0 : NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
633 : ("exit nsSubDocumentFrame::Reflow: size=%d,%d status=%x",
634 : aDesiredSize.width, aDesiredSize.height, aStatus));
635 :
636 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
637 0 : return NS_OK;
638 : }
639 :
640 : bool
641 0 : nsSubDocumentFrame::ReflowFinished()
642 : {
643 0 : if (mFrameLoader) {
644 0 : nsWeakFrame weakFrame(this);
645 :
646 0 : mFrameLoader->UpdatePositionAndSize(this);
647 :
648 0 : if (weakFrame.IsAlive()) {
649 : // Make sure that we can post a reflow callback in the future.
650 0 : mPostedReflowCallback = false;
651 : }
652 : } else {
653 0 : mPostedReflowCallback = false;
654 : }
655 0 : return false;
656 : }
657 :
658 : void
659 0 : nsSubDocumentFrame::ReflowCallbackCanceled()
660 : {
661 0 : mPostedReflowCallback = false;
662 0 : }
663 :
664 : NS_IMETHODIMP
665 0 : nsSubDocumentFrame::AttributeChanged(PRInt32 aNameSpaceID,
666 : nsIAtom* aAttribute,
667 : PRInt32 aModType)
668 : {
669 0 : if (aNameSpaceID != kNameSpaceID_None) {
670 0 : return NS_OK;
671 : }
672 :
673 : // If the noResize attribute changes, dis/allow frame to be resized
674 0 : if (aAttribute == nsGkAtoms::noresize) {
675 : // Note that we're not doing content type checks, but that's ok -- if
676 : // they'd fail we will just end up with a null framesetFrame.
677 0 : if (mContent->GetParent()->Tag() == nsGkAtoms::frameset) {
678 0 : nsIFrame* parentFrame = GetParent();
679 :
680 0 : if (parentFrame) {
681 : // There is no interface for nsHTMLFramesetFrame so QI'ing to
682 : // concrete class, yay!
683 0 : nsHTMLFramesetFrame* framesetFrame = do_QueryFrame(parentFrame);
684 0 : if (framesetFrame) {
685 0 : framesetFrame->RecalculateBorderResize();
686 : }
687 : }
688 : }
689 : }
690 0 : else if (aAttribute == nsGkAtoms::showresizer) {
691 0 : nsIFrame* rootFrame = GetSubdocumentRootFrame();
692 0 : if (rootFrame) {
693 0 : rootFrame->PresContext()->PresShell()->
694 0 : FrameNeedsReflow(rootFrame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
695 : }
696 : }
697 0 : else if (aAttribute == nsGkAtoms::marginwidth ||
698 : aAttribute == nsGkAtoms::marginheight) {
699 :
700 : // Retrieve the attributes
701 0 : nsIntSize margins = GetMarginAttributes();
702 :
703 : // Notify the frameloader
704 0 : nsRefPtr<nsFrameLoader> frameloader = FrameLoader();
705 0 : if (frameloader)
706 0 : frameloader->MarginsChanged(margins.width, margins.height);
707 : }
708 0 : else if (aAttribute == nsGkAtoms::type) {
709 0 : if (!mFrameLoader)
710 0 : return NS_OK;
711 :
712 0 : if (!mContent->IsXUL()) {
713 0 : return NS_OK;
714 : }
715 :
716 0 : if (mFrameLoader->GetRemoteBrowser()) {
717 : // TODO: Implement ContentShellAdded for remote browsers (bug 658304)
718 0 : return NS_OK;
719 : }
720 :
721 : // Note: This logic duplicates a lot of logic in
722 : // nsFrameLoader::EnsureDocShell. We should fix that.
723 :
724 : // Notify our enclosing chrome that our type has changed. We only do this
725 : // if our parent is chrome, since in all other cases we're random content
726 : // subframes and the treeowner shouldn't worry about us.
727 :
728 0 : nsCOMPtr<nsIDocShell> docShell;
729 0 : mFrameLoader->GetDocShell(getter_AddRefs(docShell));
730 0 : nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(do_QueryInterface(docShell));
731 0 : if (!docShellAsItem) {
732 0 : return NS_OK;
733 : }
734 :
735 0 : nsCOMPtr<nsIDocShellTreeItem> parentItem;
736 0 : docShellAsItem->GetParent(getter_AddRefs(parentItem));
737 0 : if (!parentItem) {
738 0 : return NS_OK;
739 : }
740 :
741 : PRInt32 parentType;
742 0 : parentItem->GetItemType(&parentType);
743 :
744 0 : if (parentType != nsIDocShellTreeItem::typeChrome) {
745 0 : return NS_OK;
746 : }
747 :
748 0 : nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
749 0 : parentItem->GetTreeOwner(getter_AddRefs(parentTreeOwner));
750 0 : if (parentTreeOwner) {
751 0 : nsAutoString value;
752 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::type, value);
753 :
754 0 : bool is_primary = value.LowerCaseEqualsLiteral("content-primary");
755 :
756 : #ifdef MOZ_XUL
757 : // when a content panel is no longer primary, hide any open popups it may have
758 0 : if (!is_primary) {
759 0 : nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
760 0 : if (pm)
761 0 : pm->HidePopupsInDocShell(docShellAsItem);
762 : }
763 : #endif
764 :
765 0 : parentTreeOwner->ContentShellRemoved(docShellAsItem);
766 :
767 0 : if (value.LowerCaseEqualsLiteral("content") ||
768 0 : StringBeginsWith(value, NS_LITERAL_STRING("content-"),
769 0 : nsCaseInsensitiveStringComparator())) {
770 : bool is_targetable = is_primary ||
771 0 : value.LowerCaseEqualsLiteral("content-targetable");
772 :
773 0 : parentTreeOwner->ContentShellAdded(docShellAsItem, is_primary,
774 0 : is_targetable, value);
775 : }
776 : }
777 : }
778 :
779 0 : return NS_OK;
780 : }
781 :
782 : nsIFrame*
783 0 : NS_NewSubDocumentFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
784 : {
785 0 : return new (aPresShell) nsSubDocumentFrame(aContext);
786 : }
787 :
788 0 : NS_IMPL_FRAMEARENA_HELPERS(nsSubDocumentFrame)
789 :
790 : void
791 0 : nsSubDocumentFrame::DestroyFrom(nsIFrame* aDestructRoot)
792 : {
793 0 : if (mPostedReflowCallback) {
794 0 : PresContext()->PresShell()->CancelReflowCallback(this);
795 0 : mPostedReflowCallback = false;
796 : }
797 :
798 0 : HideViewer();
799 :
800 0 : nsLeafFrame::DestroyFrom(aDestructRoot);
801 0 : }
802 :
803 : void
804 0 : nsSubDocumentFrame::HideViewer()
805 : {
806 0 : if (mFrameLoader && (mDidCreateDoc || mCallingShow))
807 0 : mFrameLoader->Hide();
808 0 : }
809 :
810 : nsIntSize
811 0 : nsSubDocumentFrame::GetMarginAttributes()
812 : {
813 0 : nsIntSize result(-1, -1);
814 0 : nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent);
815 0 : if (content) {
816 0 : const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::marginwidth);
817 0 : if (attr && attr->Type() == nsAttrValue::eInteger)
818 0 : result.width = attr->GetIntegerValue();
819 0 : attr = content->GetParsedAttr(nsGkAtoms::marginheight);
820 0 : if (attr && attr->Type() == nsAttrValue::eInteger)
821 0 : result.height = attr->GetIntegerValue();
822 : }
823 : return result;
824 : }
825 :
826 : nsFrameLoader*
827 0 : nsSubDocumentFrame::FrameLoader()
828 : {
829 0 : nsIContent* content = GetContent();
830 0 : if (!content)
831 0 : return nsnull;
832 :
833 0 : if (!mFrameLoader) {
834 0 : nsCOMPtr<nsIFrameLoaderOwner> loaderOwner = do_QueryInterface(content);
835 0 : if (loaderOwner) {
836 0 : nsCOMPtr<nsIFrameLoader> loader;
837 0 : loaderOwner->GetFrameLoader(getter_AddRefs(loader));
838 0 : mFrameLoader = static_cast<nsFrameLoader*>(loader.get());
839 : }
840 : }
841 0 : return mFrameLoader;
842 : }
843 :
844 : // XXX this should be called ObtainDocShell or something like that,
845 : // to indicate that it could have side effects
846 : nsresult
847 0 : nsSubDocumentFrame::GetDocShell(nsIDocShell **aDocShell)
848 : {
849 0 : *aDocShell = nsnull;
850 :
851 0 : NS_ENSURE_STATE(FrameLoader());
852 0 : return mFrameLoader->GetDocShell(aDocShell);
853 : }
854 :
855 : static void
856 0 : DestroyDisplayItemDataForFrames(nsIFrame* aFrame)
857 : {
858 0 : FrameLayerBuilder::DestroyDisplayItemDataFor(aFrame);
859 :
860 0 : nsIFrame::ChildListIterator lists(aFrame);
861 0 : for (; !lists.IsDone(); lists.Next()) {
862 0 : nsFrameList::Enumerator childFrames(lists.CurrentList());
863 0 : for (; !childFrames.AtEnd(); childFrames.Next()) {
864 0 : DestroyDisplayItemDataForFrames(childFrames.get());
865 : }
866 : }
867 0 : }
868 :
869 : static bool
870 0 : BeginSwapDocShellsForDocument(nsIDocument* aDocument, void*)
871 : {
872 0 : NS_PRECONDITION(aDocument, "");
873 :
874 0 : nsIPresShell* shell = aDocument->GetShell();
875 0 : nsIFrame* rootFrame = shell ? shell->GetRootFrame() : nsnull;
876 0 : if (rootFrame) {
877 0 : ::DestroyDisplayItemDataForFrames(rootFrame);
878 : }
879 : aDocument->EnumerateFreezableElements(
880 0 : nsObjectFrame::BeginSwapDocShells, nsnull);
881 0 : aDocument->EnumerateSubDocuments(BeginSwapDocShellsForDocument, nsnull);
882 0 : return true;
883 : }
884 :
885 : static nsIView*
886 0 : BeginSwapDocShellsForViews(nsIView* aSibling)
887 : {
888 : // Collect the removed sibling views in reverse order in 'removedViews'.
889 0 : nsIView* removedViews = nsnull;
890 0 : while (aSibling) {
891 0 : nsIDocument* doc = ::GetDocumentFromView(aSibling);
892 0 : if (doc) {
893 0 : ::BeginSwapDocShellsForDocument(doc, nsnull);
894 : }
895 0 : nsIView* next = aSibling->GetNextSibling();
896 0 : aSibling->GetViewManager()->RemoveChild(aSibling);
897 0 : aSibling->SetNextSibling(removedViews);
898 0 : removedViews = aSibling;
899 0 : aSibling = next;
900 : }
901 0 : return removedViews;
902 : }
903 :
904 : static void
905 0 : InsertViewsInReverseOrder(nsIView* aSibling, nsIView* aParent)
906 : {
907 0 : NS_PRECONDITION(aParent, "");
908 0 : NS_PRECONDITION(!aParent->GetFirstChild(), "inserting into non-empty list");
909 :
910 0 : nsIViewManager* vm = aParent->GetViewManager();
911 0 : while (aSibling) {
912 0 : nsIView* next = aSibling->GetNextSibling();
913 0 : aSibling->SetNextSibling(nsnull);
914 : // true means 'after' in document order which is 'before' in view order,
915 : // so this call prepends the child, thus reversing the siblings as we go.
916 0 : vm->InsertChild(aParent, aSibling, nsnull, true);
917 0 : aSibling = next;
918 : }
919 0 : }
920 :
921 : nsresult
922 0 : nsSubDocumentFrame::BeginSwapDocShells(nsIFrame* aOther)
923 : {
924 0 : if (!aOther || aOther->GetType() != nsGkAtoms::subDocumentFrame) {
925 0 : return NS_ERROR_NOT_IMPLEMENTED;
926 : }
927 :
928 0 : nsSubDocumentFrame* other = static_cast<nsSubDocumentFrame*>(aOther);
929 0 : if (!mFrameLoader || !mDidCreateDoc || mCallingShow ||
930 0 : !other->mFrameLoader || !other->mDidCreateDoc) {
931 0 : return NS_ERROR_NOT_IMPLEMENTED;
932 : }
933 :
934 0 : if (mInnerView && other->mInnerView) {
935 0 : nsIView* ourSubdocViews = mInnerView->GetFirstChild();
936 0 : nsIView* ourRemovedViews = ::BeginSwapDocShellsForViews(ourSubdocViews);
937 0 : nsIView* otherSubdocViews = other->mInnerView->GetFirstChild();
938 0 : nsIView* otherRemovedViews = ::BeginSwapDocShellsForViews(otherSubdocViews);
939 :
940 0 : ::InsertViewsInReverseOrder(ourRemovedViews, other->mInnerView);
941 0 : ::InsertViewsInReverseOrder(otherRemovedViews, mInnerView);
942 : }
943 0 : mFrameLoader.swap(other->mFrameLoader);
944 0 : return NS_OK;
945 : }
946 :
947 : static bool
948 0 : EndSwapDocShellsForDocument(nsIDocument* aDocument, void*)
949 : {
950 0 : NS_PRECONDITION(aDocument, "");
951 :
952 : // Our docshell and view trees have been updated for the new hierarchy.
953 : // Now also update all nsDeviceContext::mWidget to that of the
954 : // container view in the new hierarchy.
955 0 : nsCOMPtr<nsISupports> container = aDocument->GetContainer();
956 0 : nsCOMPtr<nsIDocShell> ds = do_QueryInterface(container);
957 0 : if (ds) {
958 0 : nsCOMPtr<nsIContentViewer> cv;
959 0 : ds->GetContentViewer(getter_AddRefs(cv));
960 0 : while (cv) {
961 0 : nsCOMPtr<nsPresContext> pc;
962 0 : cv->GetPresContext(getter_AddRefs(pc));
963 0 : nsDeviceContext* dc = pc ? pc->DeviceContext() : nsnull;
964 0 : if (dc) {
965 0 : nsIView* v = cv->FindContainerView();
966 0 : dc->Init(v ? v->GetNearestWidget(nsnull) : nsnull);
967 : }
968 0 : nsCOMPtr<nsIContentViewer> prev;
969 0 : cv->GetPreviousViewer(getter_AddRefs(prev));
970 0 : cv = prev;
971 : }
972 : }
973 :
974 : aDocument->EnumerateFreezableElements(
975 0 : nsObjectFrame::EndSwapDocShells, nsnull);
976 0 : aDocument->EnumerateSubDocuments(EndSwapDocShellsForDocument, nsnull);
977 0 : return true;
978 : }
979 :
980 : static void
981 0 : EndSwapDocShellsForViews(nsIView* aSibling)
982 : {
983 0 : for ( ; aSibling; aSibling = aSibling->GetNextSibling()) {
984 0 : nsIDocument* doc = ::GetDocumentFromView(aSibling);
985 0 : if (doc) {
986 0 : ::EndSwapDocShellsForDocument(doc, nsnull);
987 : }
988 : }
989 0 : }
990 :
991 : void
992 0 : nsSubDocumentFrame::EndSwapDocShells(nsIFrame* aOther)
993 : {
994 0 : nsSubDocumentFrame* other = static_cast<nsSubDocumentFrame*>(aOther);
995 0 : nsWeakFrame weakThis(this);
996 0 : nsWeakFrame weakOther(aOther);
997 :
998 0 : if (mInnerView) {
999 0 : ::EndSwapDocShellsForViews(mInnerView->GetFirstChild());
1000 : }
1001 0 : if (other->mInnerView) {
1002 0 : ::EndSwapDocShellsForViews(other->mInnerView->GetFirstChild());
1003 : }
1004 :
1005 : // Now make sure we reflow both frames, in case their contents
1006 : // determine their size.
1007 : // And repaint them, for good measure, in case there's nothing
1008 : // interesting that happens during reflow.
1009 0 : if (weakThis.IsAlive()) {
1010 0 : PresContext()->PresShell()->
1011 0 : FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
1012 0 : InvalidateFrameSubtree();
1013 : }
1014 0 : if (weakOther.IsAlive()) {
1015 0 : other->PresContext()->PresShell()->
1016 0 : FrameNeedsReflow(other, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
1017 0 : other->InvalidateFrameSubtree();
1018 : }
1019 0 : }
1020 :
1021 : nsIView*
1022 0 : nsSubDocumentFrame::EnsureInnerView()
1023 : {
1024 0 : if (mInnerView) {
1025 0 : return mInnerView;
1026 : }
1027 :
1028 : // create, init, set the parent of the view
1029 0 : nsIView* outerView = GetView();
1030 0 : NS_ASSERTION(outerView, "Must have an outer view already");
1031 0 : nsRect viewBounds(0, 0, 0, 0); // size will be fixed during reflow
1032 :
1033 0 : nsIViewManager* viewMan = outerView->GetViewManager();
1034 0 : nsIView* innerView = viewMan->CreateView(viewBounds, outerView);
1035 0 : if (!innerView) {
1036 0 : NS_ERROR("Could not create inner view");
1037 0 : return nsnull;
1038 : }
1039 0 : mInnerView = innerView;
1040 0 : viewMan->InsertChild(outerView, innerView, nsnull, true);
1041 :
1042 0 : return mInnerView;
1043 : }
1044 :
1045 : nsIFrame*
1046 0 : nsSubDocumentFrame::ObtainIntrinsicSizeFrame()
1047 : {
1048 0 : nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(GetContent());
1049 0 : if (olc) {
1050 : // We are an HTML <object>, <embed> or <applet> (a replaced element).
1051 :
1052 : // Try to get an nsIFrame for our sub-document's document element
1053 0 : nsIFrame* subDocRoot = nsnull;
1054 :
1055 0 : nsCOMPtr<nsIDocShell> docShell;
1056 0 : GetDocShell(getter_AddRefs(docShell));
1057 0 : if (docShell) {
1058 0 : nsCOMPtr<nsIPresShell> presShell;
1059 0 : docShell->GetPresShell(getter_AddRefs(presShell));
1060 0 : if (presShell) {
1061 0 : nsIScrollableFrame* scrollable = presShell->GetRootScrollFrameAsScrollable();
1062 0 : if (scrollable) {
1063 0 : nsIFrame* scrolled = scrollable->GetScrolledFrame();
1064 0 : if (scrolled) {
1065 0 : subDocRoot = scrolled->GetFirstPrincipalChild();
1066 : }
1067 : }
1068 : }
1069 : }
1070 :
1071 0 : if (subDocRoot && subDocRoot->GetContent() &&
1072 0 : subDocRoot->GetContent()->NodeInfo()->Equals(nsGkAtoms::svg, kNameSpaceID_SVG)) {
1073 0 : return subDocRoot; // SVG documents have an intrinsic size
1074 : }
1075 : }
1076 0 : return nsnull;
1077 : }
|