|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 /* rendering object that goes directly inside the document's scrollbars */ |
|
7 |
|
8 #include "nsCanvasFrame.h" |
|
9 #include "nsContainerFrame.h" |
|
10 #include "nsCSSRendering.h" |
|
11 #include "nsPresContext.h" |
|
12 #include "nsStyleContext.h" |
|
13 #include "nsRenderingContext.h" |
|
14 #include "nsGkAtoms.h" |
|
15 #include "nsIPresShell.h" |
|
16 #include "nsDisplayList.h" |
|
17 #include "nsCSSFrameConstructor.h" |
|
18 #include "nsFrameManager.h" |
|
19 #include "gfxPlatform.h" |
|
20 |
|
21 // for focus |
|
22 #include "nsIScrollableFrame.h" |
|
23 #ifdef DEBUG_CANVAS_FOCUS |
|
24 #include "nsIDocShell.h" |
|
25 #endif |
|
26 |
|
27 //#define DEBUG_CANVAS_FOCUS |
|
28 |
|
29 using namespace mozilla; |
|
30 using namespace mozilla::layout; |
|
31 using namespace mozilla::gfx; |
|
32 |
|
33 nsIFrame* |
|
34 NS_NewCanvasFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) |
|
35 { |
|
36 return new (aPresShell) nsCanvasFrame(aContext); |
|
37 } |
|
38 |
|
39 NS_IMPL_FRAMEARENA_HELPERS(nsCanvasFrame) |
|
40 |
|
41 NS_QUERYFRAME_HEAD(nsCanvasFrame) |
|
42 NS_QUERYFRAME_ENTRY(nsCanvasFrame) |
|
43 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) |
|
44 |
|
45 void |
|
46 nsCanvasFrame::DestroyFrom(nsIFrame* aDestructRoot) |
|
47 { |
|
48 nsIScrollableFrame* sf = |
|
49 PresContext()->GetPresShell()->GetRootScrollFrameAsScrollable(); |
|
50 if (sf) { |
|
51 sf->RemoveScrollPositionListener(this); |
|
52 } |
|
53 |
|
54 nsContainerFrame::DestroyFrom(aDestructRoot); |
|
55 } |
|
56 |
|
57 void |
|
58 nsCanvasFrame::ScrollPositionWillChange(nscoord aX, nscoord aY) |
|
59 { |
|
60 if (mDoPaintFocus) { |
|
61 mDoPaintFocus = false; |
|
62 PresContext()->FrameManager()->GetRootFrame()->InvalidateFrameSubtree(); |
|
63 } |
|
64 } |
|
65 |
|
66 NS_IMETHODIMP |
|
67 nsCanvasFrame::SetHasFocus(bool aHasFocus) |
|
68 { |
|
69 if (mDoPaintFocus != aHasFocus) { |
|
70 mDoPaintFocus = aHasFocus; |
|
71 PresContext()->FrameManager()->GetRootFrame()->InvalidateFrameSubtree(); |
|
72 |
|
73 if (!mAddedScrollPositionListener) { |
|
74 nsIScrollableFrame* sf = |
|
75 PresContext()->GetPresShell()->GetRootScrollFrameAsScrollable(); |
|
76 if (sf) { |
|
77 sf->AddScrollPositionListener(this); |
|
78 mAddedScrollPositionListener = true; |
|
79 } |
|
80 } |
|
81 } |
|
82 return NS_OK; |
|
83 } |
|
84 |
|
85 nsresult |
|
86 nsCanvasFrame::SetInitialChildList(ChildListID aListID, |
|
87 nsFrameList& aChildList) |
|
88 { |
|
89 NS_ASSERTION(aListID != kPrincipalList || |
|
90 aChildList.IsEmpty() || aChildList.OnlyChild(), |
|
91 "Primary child list can have at most one frame in it"); |
|
92 return nsContainerFrame::SetInitialChildList(aListID, aChildList); |
|
93 } |
|
94 |
|
95 nsresult |
|
96 nsCanvasFrame::AppendFrames(ChildListID aListID, |
|
97 nsFrameList& aFrameList) |
|
98 { |
|
99 NS_ASSERTION(aListID == kPrincipalList || |
|
100 aListID == kAbsoluteList, "unexpected child list ID"); |
|
101 NS_PRECONDITION(aListID != kAbsoluteList || |
|
102 mFrames.IsEmpty(), "already have a child frame"); |
|
103 if (aListID != kPrincipalList) { |
|
104 // We only support the Principal and Absolute child lists. |
|
105 return NS_ERROR_INVALID_ARG; |
|
106 } |
|
107 |
|
108 if (!mFrames.IsEmpty()) { |
|
109 // We only allow a single principal child frame. |
|
110 return NS_ERROR_INVALID_ARG; |
|
111 } |
|
112 |
|
113 // Insert the new frames |
|
114 NS_ASSERTION(aFrameList.FirstChild() == aFrameList.LastChild(), |
|
115 "Only one principal child frame allowed"); |
|
116 #ifdef DEBUG |
|
117 nsFrame::VerifyDirtyBitSet(aFrameList); |
|
118 #endif |
|
119 mFrames.AppendFrames(nullptr, aFrameList); |
|
120 |
|
121 PresContext()->PresShell()-> |
|
122 FrameNeedsReflow(this, nsIPresShell::eTreeChange, |
|
123 NS_FRAME_HAS_DIRTY_CHILDREN); |
|
124 |
|
125 return NS_OK; |
|
126 } |
|
127 |
|
128 nsresult |
|
129 nsCanvasFrame::InsertFrames(ChildListID aListID, |
|
130 nsIFrame* aPrevFrame, |
|
131 nsFrameList& aFrameList) |
|
132 { |
|
133 // Because we only support a single child frame inserting is the same |
|
134 // as appending |
|
135 NS_PRECONDITION(!aPrevFrame, "unexpected previous sibling frame"); |
|
136 if (aPrevFrame) |
|
137 return NS_ERROR_UNEXPECTED; |
|
138 |
|
139 return AppendFrames(aListID, aFrameList); |
|
140 } |
|
141 |
|
142 nsresult |
|
143 nsCanvasFrame::RemoveFrame(ChildListID aListID, |
|
144 nsIFrame* aOldFrame) |
|
145 { |
|
146 NS_ASSERTION(aListID == kPrincipalList || |
|
147 aListID == kAbsoluteList, "unexpected child list ID"); |
|
148 if (aListID != kPrincipalList && aListID != kAbsoluteList) { |
|
149 // We only support the Principal and Absolute child lists. |
|
150 return NS_ERROR_INVALID_ARG; |
|
151 } |
|
152 |
|
153 if (aOldFrame != mFrames.FirstChild()) |
|
154 return NS_ERROR_FAILURE; |
|
155 |
|
156 // Remove the frame and destroy it |
|
157 mFrames.DestroyFrame(aOldFrame); |
|
158 |
|
159 PresContext()->PresShell()-> |
|
160 FrameNeedsReflow(this, nsIPresShell::eTreeChange, |
|
161 NS_FRAME_HAS_DIRTY_CHILDREN); |
|
162 return NS_OK; |
|
163 } |
|
164 |
|
165 nsRect nsCanvasFrame::CanvasArea() const |
|
166 { |
|
167 // Not clear which overflow rect we want here, but it probably doesn't |
|
168 // matter. |
|
169 nsRect result(GetVisualOverflowRect()); |
|
170 |
|
171 nsIScrollableFrame *scrollableFrame = do_QueryFrame(GetParent()); |
|
172 if (scrollableFrame) { |
|
173 nsRect portRect = scrollableFrame->GetScrollPortRect(); |
|
174 result.UnionRect(result, nsRect(nsPoint(0, 0), portRect.Size())); |
|
175 } |
|
176 return result; |
|
177 } |
|
178 |
|
179 void |
|
180 nsDisplayCanvasBackgroundColor::Paint(nsDisplayListBuilder* aBuilder, |
|
181 nsRenderingContext* aCtx) |
|
182 { |
|
183 nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame); |
|
184 nsPoint offset = ToReferenceFrame(); |
|
185 nsRect bgClipRect = frame->CanvasArea() + offset; |
|
186 if (NS_GET_A(mColor) > 0) { |
|
187 aCtx->SetColor(mColor); |
|
188 aCtx->FillRect(bgClipRect); |
|
189 } |
|
190 } |
|
191 |
|
192 static void BlitSurface(gfxContext* aDest, const gfxRect& aRect, gfxASurface* aSource) |
|
193 { |
|
194 aDest->Translate(gfxPoint(aRect.x, aRect.y)); |
|
195 aDest->SetSource(aSource); |
|
196 aDest->NewPath(); |
|
197 aDest->Rectangle(gfxRect(0, 0, aRect.width, aRect.height)); |
|
198 aDest->Fill(); |
|
199 aDest->Translate(-gfxPoint(aRect.x, aRect.y)); |
|
200 } |
|
201 |
|
202 static void BlitSurface(DrawTarget* aDest, const gfxRect& aRect, DrawTarget* aSource) |
|
203 { |
|
204 RefPtr<SourceSurface> source = aSource->Snapshot(); |
|
205 aDest->DrawSurface(source, |
|
206 Rect(aRect.x, aRect.y, aRect.width, aRect.height), |
|
207 Rect(0, 0, aRect.width, aRect.height)); |
|
208 } |
|
209 |
|
210 void |
|
211 nsDisplayCanvasBackgroundImage::Paint(nsDisplayListBuilder* aBuilder, |
|
212 nsRenderingContext* aCtx) |
|
213 { |
|
214 nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame); |
|
215 nsPoint offset = ToReferenceFrame(); |
|
216 nsRect bgClipRect = frame->CanvasArea() + offset; |
|
217 |
|
218 nsRefPtr<nsRenderingContext> context; |
|
219 nsRefPtr<gfxContext> dest = aCtx->ThebesContext(); |
|
220 nsRefPtr<gfxASurface> surf; |
|
221 RefPtr<DrawTarget> dt; |
|
222 nsRefPtr<gfxContext> ctx; |
|
223 gfxRect destRect; |
|
224 #ifndef MOZ_GFX_OPTIMIZE_MOBILE |
|
225 if (IsSingleFixedPositionImage(aBuilder, bgClipRect, &destRect) && |
|
226 aBuilder->IsPaintingToWindow() && !aBuilder->IsCompositingCheap() && |
|
227 !dest->CurrentMatrix().HasNonIntegerTranslation()) { |
|
228 // Snap image rectangle to nearest pixel boundaries. This is the right way |
|
229 // to snap for this context, because we checked HasNonIntegerTranslation above. |
|
230 destRect.Round(); |
|
231 if (dest->IsCairo()) { |
|
232 surf = static_cast<gfxASurface*>(Frame()->Properties().Get(nsIFrame::CachedBackgroundImage())); |
|
233 nsRefPtr<gfxASurface> destSurf = dest->CurrentSurface(); |
|
234 if (surf && surf->GetType() == destSurf->GetType()) { |
|
235 BlitSurface(dest, destRect, surf); |
|
236 return; |
|
237 } |
|
238 surf = destSurf->CreateSimilarSurface( |
|
239 gfxContentType::COLOR_ALPHA, |
|
240 gfxIntSize(ceil(destRect.width), ceil(destRect.height))); |
|
241 } else { |
|
242 dt = static_cast<DrawTarget*>(Frame()->Properties().Get(nsIFrame::CachedBackgroundImageDT())); |
|
243 DrawTarget* destDT = dest->GetDrawTarget(); |
|
244 if (dt) { |
|
245 BlitSurface(destDT, destRect, dt); |
|
246 return; |
|
247 } |
|
248 dt = destDT->CreateSimilarDrawTarget(IntSize(ceil(destRect.width), ceil(destRect.height)), SurfaceFormat::B8G8R8A8); |
|
249 } |
|
250 if (surf || dt) { |
|
251 if (surf) { |
|
252 ctx = new gfxContext(surf); |
|
253 } else { |
|
254 ctx = new gfxContext(dt); |
|
255 } |
|
256 ctx->Translate(-gfxPoint(destRect.x, destRect.y)); |
|
257 context = new nsRenderingContext(); |
|
258 context->Init(aCtx->DeviceContext(), ctx); |
|
259 } |
|
260 } |
|
261 #endif |
|
262 |
|
263 PaintInternal(aBuilder, |
|
264 (surf || dt) ? context.get() : aCtx, |
|
265 (surf || dt) ? bgClipRect: mVisibleRect, |
|
266 &bgClipRect); |
|
267 |
|
268 if (surf) { |
|
269 BlitSurface(dest, destRect, surf); |
|
270 frame->Properties().Set(nsIFrame::CachedBackgroundImage(), |
|
271 surf.forget().take()); |
|
272 } |
|
273 if (dt) { |
|
274 BlitSurface(dest->GetDrawTarget(), destRect, dt); |
|
275 frame->Properties().Set(nsIFrame::CachedBackgroundImageDT(), dt.forget().drop()); |
|
276 } |
|
277 } |
|
278 |
|
279 void |
|
280 nsDisplayCanvasThemedBackground::Paint(nsDisplayListBuilder* aBuilder, |
|
281 nsRenderingContext* aCtx) |
|
282 { |
|
283 nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame); |
|
284 nsPoint offset = ToReferenceFrame(); |
|
285 nsRect bgClipRect = frame->CanvasArea() + offset; |
|
286 |
|
287 PaintInternal(aBuilder, aCtx, mVisibleRect, &bgClipRect); |
|
288 } |
|
289 |
|
290 /** |
|
291 * A display item to paint the focus ring for the document. |
|
292 * |
|
293 * The only reason this can't use nsDisplayGeneric is overriding GetBounds. |
|
294 */ |
|
295 class nsDisplayCanvasFocus : public nsDisplayItem { |
|
296 public: |
|
297 nsDisplayCanvasFocus(nsDisplayListBuilder* aBuilder, nsCanvasFrame *aFrame) |
|
298 : nsDisplayItem(aBuilder, aFrame) |
|
299 { |
|
300 MOZ_COUNT_CTOR(nsDisplayCanvasFocus); |
|
301 } |
|
302 virtual ~nsDisplayCanvasFocus() { |
|
303 MOZ_COUNT_DTOR(nsDisplayCanvasFocus); |
|
304 } |
|
305 |
|
306 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, |
|
307 bool* aSnap) MOZ_OVERRIDE |
|
308 { |
|
309 *aSnap = false; |
|
310 // This is an overestimate, but that's not a problem. |
|
311 nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame); |
|
312 return frame->CanvasArea() + ToReferenceFrame(); |
|
313 } |
|
314 |
|
315 virtual void Paint(nsDisplayListBuilder* aBuilder, |
|
316 nsRenderingContext* aCtx) MOZ_OVERRIDE |
|
317 { |
|
318 nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame); |
|
319 frame->PaintFocus(*aCtx, ToReferenceFrame()); |
|
320 } |
|
321 |
|
322 NS_DISPLAY_DECL_NAME("CanvasFocus", TYPE_CANVAS_FOCUS) |
|
323 }; |
|
324 |
|
325 void |
|
326 nsCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
|
327 const nsRect& aDirtyRect, |
|
328 const nsDisplayListSet& aLists) |
|
329 { |
|
330 if (GetPrevInFlow()) { |
|
331 DisplayOverflowContainers(aBuilder, aDirtyRect, aLists); |
|
332 } |
|
333 |
|
334 // Force a background to be shown. We may have a background propagated to us, |
|
335 // in which case StyleBackground wouldn't have the right background |
|
336 // and the code in nsFrame::DisplayBorderBackgroundOutline might not give us |
|
337 // a background. |
|
338 // We don't have any border or outline, and our background draws over |
|
339 // the overflow area, so just add nsDisplayCanvasBackground instead of |
|
340 // calling DisplayBorderBackgroundOutline. |
|
341 if (IsVisibleForPainting(aBuilder)) { |
|
342 nsStyleContext* bgSC; |
|
343 const nsStyleBackground* bg = nullptr; |
|
344 bool isThemed = IsThemed(); |
|
345 if (!isThemed && nsCSSRendering::FindBackground(this, &bgSC)) { |
|
346 bg = bgSC->StyleBackground(); |
|
347 } |
|
348 aLists.BorderBackground()->AppendNewToTop( |
|
349 new (aBuilder) nsDisplayCanvasBackgroundColor(aBuilder, this)); |
|
350 |
|
351 if (isThemed) { |
|
352 aLists.BorderBackground()->AppendNewToTop( |
|
353 new (aBuilder) nsDisplayCanvasThemedBackground(aBuilder, this)); |
|
354 return; |
|
355 } |
|
356 |
|
357 if (!bg) { |
|
358 return; |
|
359 } |
|
360 |
|
361 // Create separate items for each background layer. |
|
362 NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) { |
|
363 if (bg->mLayers[i].mImage.IsEmpty()) { |
|
364 continue; |
|
365 } |
|
366 aLists.BorderBackground()->AppendNewToTop( |
|
367 new (aBuilder) nsDisplayCanvasBackgroundImage(aBuilder, this, i, bg)); |
|
368 } |
|
369 } |
|
370 |
|
371 nsIFrame* kid; |
|
372 for (kid = GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) { |
|
373 // Put our child into its own pseudo-stack. |
|
374 BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); |
|
375 } |
|
376 |
|
377 #ifdef DEBUG_CANVAS_FOCUS |
|
378 nsCOMPtr<nsIContent> focusContent; |
|
379 aPresContext->EventStateManager()-> |
|
380 GetFocusedContent(getter_AddRefs(focusContent)); |
|
381 |
|
382 bool hasFocus = false; |
|
383 nsCOMPtr<nsISupports> container; |
|
384 aPresContext->GetContainer(getter_AddRefs(container)); |
|
385 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container)); |
|
386 if (docShell) { |
|
387 docShell->GetHasFocus(&hasFocus); |
|
388 printf("%p - nsCanvasFrame::Paint R:%d,%d,%d,%d DR: %d,%d,%d,%d\n", this, |
|
389 mRect.x, mRect.y, mRect.width, mRect.height, |
|
390 aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height); |
|
391 } |
|
392 printf("%p - Focus: %s c: %p DoPaint:%s\n", docShell.get(), hasFocus?"Y":"N", |
|
393 focusContent.get(), mDoPaintFocus?"Y":"N"); |
|
394 #endif |
|
395 |
|
396 if (!mDoPaintFocus) |
|
397 return; |
|
398 // Only paint the focus if we're visible |
|
399 if (!StyleVisibility()->IsVisible()) |
|
400 return; |
|
401 |
|
402 aLists.Outlines()->AppendNewToTop(new (aBuilder) |
|
403 nsDisplayCanvasFocus(aBuilder, this)); |
|
404 } |
|
405 |
|
406 void |
|
407 nsCanvasFrame::PaintFocus(nsRenderingContext& aRenderingContext, nsPoint aPt) |
|
408 { |
|
409 nsRect focusRect(aPt, GetSize()); |
|
410 |
|
411 nsIScrollableFrame *scrollableFrame = do_QueryFrame(GetParent()); |
|
412 if (scrollableFrame) { |
|
413 nsRect portRect = scrollableFrame->GetScrollPortRect(); |
|
414 focusRect.width = portRect.width; |
|
415 focusRect.height = portRect.height; |
|
416 focusRect.MoveBy(scrollableFrame->GetScrollPosition()); |
|
417 } |
|
418 |
|
419 // XXX use the root frame foreground color, but should we find BODY frame |
|
420 // for HTML documents? |
|
421 nsIFrame* root = mFrames.FirstChild(); |
|
422 const nsStyleColor* color = root ? root->StyleColor() : StyleColor(); |
|
423 if (!color) { |
|
424 NS_ERROR("current color cannot be found"); |
|
425 return; |
|
426 } |
|
427 |
|
428 nsCSSRendering::PaintFocus(PresContext(), aRenderingContext, |
|
429 focusRect, color->mColor); |
|
430 } |
|
431 |
|
432 /* virtual */ nscoord |
|
433 nsCanvasFrame::GetMinWidth(nsRenderingContext *aRenderingContext) |
|
434 { |
|
435 nscoord result; |
|
436 DISPLAY_MIN_WIDTH(this, result); |
|
437 if (mFrames.IsEmpty()) |
|
438 result = 0; |
|
439 else |
|
440 result = mFrames.FirstChild()->GetMinWidth(aRenderingContext); |
|
441 return result; |
|
442 } |
|
443 |
|
444 /* virtual */ nscoord |
|
445 nsCanvasFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) |
|
446 { |
|
447 nscoord result; |
|
448 DISPLAY_PREF_WIDTH(this, result); |
|
449 if (mFrames.IsEmpty()) |
|
450 result = 0; |
|
451 else |
|
452 result = mFrames.FirstChild()->GetPrefWidth(aRenderingContext); |
|
453 return result; |
|
454 } |
|
455 |
|
456 nsresult |
|
457 nsCanvasFrame::Reflow(nsPresContext* aPresContext, |
|
458 nsHTMLReflowMetrics& aDesiredSize, |
|
459 const nsHTMLReflowState& aReflowState, |
|
460 nsReflowStatus& aStatus) |
|
461 { |
|
462 DO_GLOBAL_REFLOW_COUNT("nsCanvasFrame"); |
|
463 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); |
|
464 NS_FRAME_TRACE_REFLOW_IN("nsCanvasFrame::Reflow"); |
|
465 |
|
466 // Initialize OUT parameter |
|
467 aStatus = NS_FRAME_COMPLETE; |
|
468 |
|
469 nsCanvasFrame* prevCanvasFrame = static_cast<nsCanvasFrame*> |
|
470 (GetPrevInFlow()); |
|
471 if (prevCanvasFrame) { |
|
472 AutoFrameListPtr overflow(aPresContext, |
|
473 prevCanvasFrame->StealOverflowFrames()); |
|
474 if (overflow) { |
|
475 NS_ASSERTION(overflow->OnlyChild(), |
|
476 "must have doc root as canvas frame's only child"); |
|
477 nsContainerFrame::ReparentFrameViewList(*overflow, prevCanvasFrame, this); |
|
478 // Prepend overflow to the our child list. There may already be |
|
479 // children placeholders for fixed-pos elements, which don't get |
|
480 // reflowed but must not be lost until the canvas frame is destroyed. |
|
481 mFrames.InsertFrames(this, nullptr, *overflow); |
|
482 } |
|
483 } |
|
484 |
|
485 // Set our size up front, since some parts of reflow depend on it |
|
486 // being already set. Note that the computed height may be |
|
487 // unconstrained; that's ok. Consumers should watch out for that. |
|
488 SetSize(nsSize(aReflowState.ComputedWidth(), aReflowState.ComputedHeight())); |
|
489 |
|
490 // Reflow our one and only normal child frame. It's either the root |
|
491 // element's frame or a placeholder for that frame, if the root element |
|
492 // is abs-pos or fixed-pos. We may have additional children which |
|
493 // are placeholders for continuations of fixed-pos content, but those |
|
494 // don't need to be reflowed. The normal child is always comes before |
|
495 // the fixed-pos placeholders, because we insert it at the start |
|
496 // of the child list, above. |
|
497 nsHTMLReflowMetrics kidDesiredSize(aReflowState); |
|
498 if (mFrames.IsEmpty()) { |
|
499 // We have no child frame, so return an empty size |
|
500 aDesiredSize.Width() = aDesiredSize.Height() = 0; |
|
501 } else { |
|
502 nsIFrame* kidFrame = mFrames.FirstChild(); |
|
503 bool kidDirty = (kidFrame->GetStateBits() & NS_FRAME_IS_DIRTY) != 0; |
|
504 |
|
505 nsHTMLReflowState kidReflowState(aPresContext, aReflowState, kidFrame, |
|
506 nsSize(aReflowState.AvailableWidth(), |
|
507 aReflowState.AvailableHeight())); |
|
508 |
|
509 if (aReflowState.mFlags.mVResize && |
|
510 (kidFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)) { |
|
511 // Tell our kid it's being vertically resized too. Bit of a |
|
512 // hack for framesets. |
|
513 kidReflowState.mFlags.mVResize = true; |
|
514 } |
|
515 |
|
516 nsPoint kidPt(kidReflowState.ComputedPhysicalMargin().left, |
|
517 kidReflowState.ComputedPhysicalMargin().top); |
|
518 |
|
519 kidReflowState.ApplyRelativePositioning(&kidPt); |
|
520 |
|
521 // Reflow the frame |
|
522 ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowState, |
|
523 kidPt.x, kidPt.y, 0, aStatus); |
|
524 |
|
525 // Complete the reflow and position and size the child frame |
|
526 FinishReflowChild(kidFrame, aPresContext, kidDesiredSize, &kidReflowState, |
|
527 kidPt.x, kidPt.y, 0); |
|
528 |
|
529 if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) { |
|
530 nsIFrame* nextFrame = kidFrame->GetNextInFlow(); |
|
531 NS_ASSERTION(nextFrame || aStatus & NS_FRAME_REFLOW_NEXTINFLOW, |
|
532 "If it's incomplete and has no nif yet, it must flag a nif reflow."); |
|
533 if (!nextFrame) { |
|
534 nextFrame = aPresContext->PresShell()->FrameConstructor()-> |
|
535 CreateContinuingFrame(aPresContext, kidFrame, this); |
|
536 SetOverflowFrames(nsFrameList(nextFrame, nextFrame)); |
|
537 // Root overflow containers will be normal children of |
|
538 // the canvas frame, but that's ok because there |
|
539 // aren't any other frames we need to isolate them from |
|
540 // during reflow. |
|
541 } |
|
542 if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) { |
|
543 nextFrame->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); |
|
544 } |
|
545 } |
|
546 |
|
547 // If the child frame was just inserted, then we're responsible for making sure |
|
548 // it repaints |
|
549 if (kidDirty) { |
|
550 // But we have a new child, which will affect our background, so |
|
551 // invalidate our whole rect. |
|
552 // Note: Even though we request to be sized to our child's size, our |
|
553 // scroll frame ensures that we are always the size of the viewport. |
|
554 // Also note: GetPosition() on a CanvasFrame is always going to return |
|
555 // (0, 0). We only want to invalidate GetRect() since Get*OverflowRect() |
|
556 // could also include overflow to our top and left (out of the viewport) |
|
557 // which doesn't need to be painted. |
|
558 nsIFrame* viewport = PresContext()->GetPresShell()->GetRootFrame(); |
|
559 viewport->InvalidateFrame(); |
|
560 } |
|
561 |
|
562 // Return our desired size. Normally it's what we're told, but |
|
563 // sometimes we can be given an unconstrained height (when a window |
|
564 // is sizing-to-content), and we should compute our desired height. |
|
565 aDesiredSize.Width() = aReflowState.ComputedWidth(); |
|
566 if (aReflowState.ComputedHeight() == NS_UNCONSTRAINEDSIZE) { |
|
567 aDesiredSize.Height() = kidFrame->GetRect().height + |
|
568 kidReflowState.ComputedPhysicalMargin().TopBottom(); |
|
569 } else { |
|
570 aDesiredSize.Height() = aReflowState.ComputedHeight(); |
|
571 } |
|
572 |
|
573 aDesiredSize.SetOverflowAreasToDesiredBounds(); |
|
574 aDesiredSize.mOverflowAreas.UnionWith( |
|
575 kidDesiredSize.mOverflowAreas + kidPt); |
|
576 } |
|
577 |
|
578 if (prevCanvasFrame) { |
|
579 ReflowOverflowContainerChildren(aPresContext, aReflowState, |
|
580 aDesiredSize.mOverflowAreas, 0, |
|
581 aStatus); |
|
582 } |
|
583 |
|
584 FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus); |
|
585 |
|
586 NS_FRAME_TRACE_REFLOW_OUT("nsCanvasFrame::Reflow", aStatus); |
|
587 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); |
|
588 return NS_OK; |
|
589 } |
|
590 |
|
591 nsIAtom* |
|
592 nsCanvasFrame::GetType() const |
|
593 { |
|
594 return nsGkAtoms::canvasFrame; |
|
595 } |
|
596 |
|
597 nsresult |
|
598 nsCanvasFrame::GetContentForEvent(WidgetEvent* aEvent, |
|
599 nsIContent** aContent) |
|
600 { |
|
601 NS_ENSURE_ARG_POINTER(aContent); |
|
602 nsresult rv = nsFrame::GetContentForEvent(aEvent, |
|
603 aContent); |
|
604 if (NS_FAILED(rv) || !*aContent) { |
|
605 nsIFrame* kid = mFrames.FirstChild(); |
|
606 if (kid) { |
|
607 rv = kid->GetContentForEvent(aEvent, |
|
608 aContent); |
|
609 } |
|
610 } |
|
611 |
|
612 return rv; |
|
613 } |
|
614 |
|
615 #ifdef DEBUG_FRAME_DUMP |
|
616 nsresult |
|
617 nsCanvasFrame::GetFrameName(nsAString& aResult) const |
|
618 { |
|
619 return MakeFrameName(NS_LITERAL_STRING("Canvas"), aResult); |
|
620 } |
|
621 #endif |