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
10 #include "AccessibleCaretEventHub.h"
11 #include "gfxUtils.h"
12 #include "nsContainerFrame.h"
13 #include "nsCSSRendering.h"
14 #include "nsPresContext.h"
15 #include "nsStyleContext.h"
16 #include "nsRenderingContext.h"
17 #include "nsGkAtoms.h"
18 #include "nsPresShell.h"
19 #include "nsIPresShell.h"
20 #include "nsDisplayList.h"
21 #include "nsCSSFrameConstructor.h"
22 #include "nsFrameManager.h"
23 #include "gfxPlatform.h"
24 #include "nsPrintfCString.h"
25 #include "mozilla/dom/AnonymousContent.h"
26 // for focus
27 #include "nsIScrollableFrame.h"
28 #ifdef DEBUG_CANVAS_FOCUS
29 #include "nsIDocShell.h"
30 #endif
31
32 //#define DEBUG_CANVAS_FOCUS
33
34 using namespace mozilla;
35 using namespace mozilla::dom;
36 using namespace mozilla::layout;
37 using namespace mozilla::gfx;
38
39 nsCanvasFrame*
NS_NewCanvasFrame(nsIPresShell * aPresShell,nsStyleContext * aContext)40 NS_NewCanvasFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
41 {
42 return new (aPresShell) nsCanvasFrame(aContext);
43 }
44
45 NS_IMPL_FRAMEARENA_HELPERS(nsCanvasFrame)
46
NS_QUERYFRAME_HEAD(nsCanvasFrame)47 NS_QUERYFRAME_HEAD(nsCanvasFrame)
48 NS_QUERYFRAME_ENTRY(nsCanvasFrame)
49 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
50 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
51
52 void
53 nsCanvasFrame::ShowCustomContentContainer()
54 {
55 if (mCustomContentContainer) {
56 mCustomContentContainer->UnsetAttr(kNameSpaceID_None, nsGkAtoms::hidden, true);
57 }
58 }
59
60 void
HideCustomContentContainer()61 nsCanvasFrame::HideCustomContentContainer()
62 {
63 if (mCustomContentContainer) {
64 mCustomContentContainer->SetAttr(kNameSpaceID_None, nsGkAtoms::hidden,
65 NS_LITERAL_STRING("true"),
66 true);
67 }
68 }
69
70 nsresult
CreateAnonymousContent(nsTArray<ContentInfo> & aElements)71 nsCanvasFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
72 {
73 if (!mContent) {
74 return NS_OK;
75 }
76
77 nsCOMPtr<nsIDocument> doc = mContent->OwnerDoc();
78 nsresult rv = NS_OK;
79
80 // Create the custom content container.
81 mCustomContentContainer = doc->CreateHTMLElement(nsGkAtoms::div);
82 #ifdef DEBUG
83 // We restyle our mCustomContentContainer, even though it's root anonymous
84 // content. Normally that's not OK because the frame constructor doesn't know
85 // how to order the frame tree in such cases, but we make this work for this
86 // particular case, so it's OK.
87 mCustomContentContainer->SetProperty(nsGkAtoms::restylableAnonymousNode,
88 reinterpret_cast<void*>(true));
89 #endif // DEBUG
90
91 aElements.AppendElement(mCustomContentContainer);
92
93 // XXX add :moz-native-anonymous or will that be automatically set?
94 rv = mCustomContentContainer->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
95 NS_LITERAL_STRING("moz-custom-content-container"),
96 true);
97 NS_ENSURE_SUCCESS(rv, rv);
98
99 // Append all existing AnonymousContent nodes stored at document level if any.
100 size_t len = doc->GetAnonymousContents().Length();
101 for (size_t i = 0; i < len; ++i) {
102 nsCOMPtr<Element> node = doc->GetAnonymousContents()[i]->GetContentNode();
103 mCustomContentContainer->AppendChildTo(node->AsContent(), true);
104 }
105
106 // Only create a frame for mCustomContentContainer if it has some children.
107 if (len == 0) {
108 HideCustomContentContainer();
109 }
110
111 RefPtr<AccessibleCaretEventHub> eventHub =
112 PresContext()->GetPresShell()->GetAccessibleCaretEventHub();
113 if (eventHub) {
114 // AccessibleCaret will insert anonymous caret elements.
115 eventHub->Init();
116 }
117
118 return NS_OK;
119 }
120
121 void
AppendAnonymousContentTo(nsTArray<nsIContent * > & aElements,uint32_t aFilter)122 nsCanvasFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements, uint32_t aFilter)
123 {
124 aElements.AppendElement(mCustomContentContainer);
125 }
126
127 void
DestroyFrom(nsIFrame * aDestructRoot)128 nsCanvasFrame::DestroyFrom(nsIFrame* aDestructRoot)
129 {
130 nsIScrollableFrame* sf =
131 PresContext()->GetPresShell()->GetRootScrollFrameAsScrollable();
132 if (sf) {
133 sf->RemoveScrollPositionListener(this);
134 }
135
136 // Elements inserted in the custom content container have the same lifetime as
137 // the document, so before destroying the container, make sure to keep a clone
138 // of each of them at document level so they can be re-appended on reframe.
139 if (mCustomContentContainer) {
140 nsCOMPtr<nsIDocument> doc = mContent->OwnerDoc();
141 ErrorResult rv;
142
143 nsTArray<RefPtr<mozilla::dom::AnonymousContent>>& docAnonContents =
144 doc->GetAnonymousContents();
145 for (size_t i = 0, len = docAnonContents.Length(); i < len; ++i) {
146 AnonymousContent* content = docAnonContents[i];
147 nsCOMPtr<nsINode> clonedElement = content->GetContentNode()->CloneNode(true, rv);
148 content->SetContentNode(clonedElement->AsElement());
149 }
150 }
151 nsContentUtils::DestroyAnonymousContent(&mCustomContentContainer);
152
153 nsContainerFrame::DestroyFrom(aDestructRoot);
154 }
155
156 void
ScrollPositionWillChange(nscoord aX,nscoord aY)157 nsCanvasFrame::ScrollPositionWillChange(nscoord aX, nscoord aY)
158 {
159 if (mDoPaintFocus) {
160 mDoPaintFocus = false;
161 PresContext()->FrameManager()->GetRootFrame()->InvalidateFrameSubtree();
162 }
163 }
164
165 NS_IMETHODIMP
SetHasFocus(bool aHasFocus)166 nsCanvasFrame::SetHasFocus(bool aHasFocus)
167 {
168 if (mDoPaintFocus != aHasFocus) {
169 mDoPaintFocus = aHasFocus;
170 PresContext()->FrameManager()->GetRootFrame()->InvalidateFrameSubtree();
171
172 if (!mAddedScrollPositionListener) {
173 nsIScrollableFrame* sf =
174 PresContext()->GetPresShell()->GetRootScrollFrameAsScrollable();
175 if (sf) {
176 sf->AddScrollPositionListener(this);
177 mAddedScrollPositionListener = true;
178 }
179 }
180 }
181 return NS_OK;
182 }
183
184 #ifdef DEBUG
185 void
SetInitialChildList(ChildListID aListID,nsFrameList & aChildList)186 nsCanvasFrame::SetInitialChildList(ChildListID aListID,
187 nsFrameList& aChildList)
188 {
189 NS_ASSERTION(aListID != kPrincipalList ||
190 aChildList.IsEmpty() || aChildList.OnlyChild(),
191 "Primary child list can have at most one frame in it");
192 nsContainerFrame::SetInitialChildList(aListID, aChildList);
193 }
194
195 void
AppendFrames(ChildListID aListID,nsFrameList & aFrameList)196 nsCanvasFrame::AppendFrames(ChildListID aListID,
197 nsFrameList& aFrameList)
198 {
199 MOZ_ASSERT(aListID == kPrincipalList, "unexpected child list");
200 if (!mFrames.IsEmpty()) {
201 for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
202 // We only allow native anonymous child frames to be in principal child
203 // list in canvas frame.
204 MOZ_ASSERT(e.get()->GetContent()->IsInNativeAnonymousSubtree(),
205 "invalid child list");
206 }
207 }
208 nsFrame::VerifyDirtyBitSet(aFrameList);
209 nsContainerFrame::AppendFrames(aListID, aFrameList);
210 }
211
212 void
InsertFrames(ChildListID aListID,nsIFrame * aPrevFrame,nsFrameList & aFrameList)213 nsCanvasFrame::InsertFrames(ChildListID aListID,
214 nsIFrame* aPrevFrame,
215 nsFrameList& aFrameList)
216 {
217 // Because we only support a single child frame inserting is the same
218 // as appending
219 MOZ_ASSERT(!aPrevFrame, "unexpected previous sibling frame");
220 AppendFrames(aListID, aFrameList);
221 }
222
223 void
RemoveFrame(ChildListID aListID,nsIFrame * aOldFrame)224 nsCanvasFrame::RemoveFrame(ChildListID aListID,
225 nsIFrame* aOldFrame)
226 {
227 MOZ_ASSERT(aListID == kPrincipalList, "unexpected child list");
228 nsContainerFrame::RemoveFrame(aListID, aOldFrame);
229 }
230 #endif
231
CanvasArea() const232 nsRect nsCanvasFrame::CanvasArea() const
233 {
234 // Not clear which overflow rect we want here, but it probably doesn't
235 // matter.
236 nsRect result(GetVisualOverflowRect());
237
238 nsIScrollableFrame *scrollableFrame = do_QueryFrame(GetParent());
239 if (scrollableFrame) {
240 nsRect portRect = scrollableFrame->GetScrollPortRect();
241 result.UnionRect(result, nsRect(nsPoint(0, 0), portRect.Size()));
242 }
243 return result;
244 }
245
246 void
Paint(nsDisplayListBuilder * aBuilder,nsRenderingContext * aCtx)247 nsDisplayCanvasBackgroundColor::Paint(nsDisplayListBuilder* aBuilder,
248 nsRenderingContext* aCtx)
249 {
250 nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
251 nsPoint offset = ToReferenceFrame();
252 nsRect bgClipRect = frame->CanvasArea() + offset;
253 if (NS_GET_A(mColor) > 0) {
254 DrawTarget* drawTarget = aCtx->GetDrawTarget();
255 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
256 Rect devPxRect =
257 NSRectToSnappedRect(bgClipRect, appUnitsPerDevPixel, *drawTarget);
258 drawTarget->FillRect(devPxRect, ColorPattern(ToDeviceColor(mColor)));
259 }
260 }
261
262 #ifdef MOZ_DUMP_PAINTING
263 void
WriteDebugInfo(std::stringstream & aStream)264 nsDisplayCanvasBackgroundColor::WriteDebugInfo(std::stringstream& aStream)
265 {
266 aStream << " (rgba "
267 << (int)NS_GET_R(mColor) << ","
268 << (int)NS_GET_G(mColor) << ","
269 << (int)NS_GET_B(mColor) << ","
270 << (int)NS_GET_A(mColor) << ")";
271 }
272 #endif
273
274 #ifndef MOZ_GFX_OPTIMIZE_MOBILE
BlitSurface(DrawTarget * aDest,const gfxRect & aRect,DrawTarget * aSource)275 static void BlitSurface(DrawTarget* aDest, const gfxRect& aRect, DrawTarget* aSource)
276 {
277 RefPtr<SourceSurface> source = aSource->Snapshot();
278 aDest->DrawSurface(source,
279 Rect(aRect.x, aRect.y, aRect.width, aRect.height),
280 Rect(0, 0, aRect.width, aRect.height));
281 }
282 #endif
283
284 void
Paint(nsDisplayListBuilder * aBuilder,nsRenderingContext * aCtx)285 nsDisplayCanvasBackgroundImage::Paint(nsDisplayListBuilder* aBuilder,
286 nsRenderingContext* aCtx)
287 {
288 nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
289 nsPoint offset = ToReferenceFrame();
290 nsRect bgClipRect = frame->CanvasArea() + offset;
291
292 #ifndef MOZ_GFX_OPTIMIZE_MOBILE
293 RefPtr<gfxContext> dest = aCtx->ThebesContext();
294 gfxRect destRect;
295 if (IsSingleFixedPositionImage(aBuilder, bgClipRect, &destRect) &&
296 aBuilder->IsPaintingToWindow() && !aBuilder->IsCompositingCheap() &&
297 !dest->CurrentMatrix().HasNonIntegerTranslation()) {
298 // Snap image rectangle to nearest pixel boundaries. This is the right way
299 // to snap for this context, because we checked HasNonIntegerTranslation
300 // above.
301 destRect.Round();
302 RefPtr<DrawTarget> dt =
303 Frame()->Properties().Get(nsIFrame::CachedBackgroundImageDT());
304 DrawTarget* destDT = dest->GetDrawTarget();
305 if (dt) {
306 BlitSurface(destDT, destRect, dt);
307 return;
308 }
309
310 dt = destDT->CreateSimilarDrawTarget(IntSize::Ceil(destRect.width,
311 destRect.height),
312 SurfaceFormat::B8G8R8A8);
313 if (dt && dt->IsValid()) {
314 RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
315 MOZ_ASSERT(ctx); // already checked draw target above
316 ctx->SetMatrix(ctx->CurrentMatrix().Translate(-destRect.x, -destRect.y));
317 nsRenderingContext context(ctx);
318 PaintInternal(aBuilder, &context, bgClipRect, &bgClipRect);
319 BlitSurface(dest->GetDrawTarget(), destRect, dt);
320 frame->Properties().Set(nsIFrame::CachedBackgroundImageDT(),
321 dt.forget().take());
322 return;
323 }
324 }
325 #endif
326 PaintInternal(aBuilder, aCtx, mVisibleRect, &bgClipRect);
327 }
328
329 bool
IsSingleFixedPositionImage(nsDisplayListBuilder * aBuilder,const nsRect & aClipRect,gfxRect * aDestRect)330 nsDisplayCanvasBackgroundImage::IsSingleFixedPositionImage(nsDisplayListBuilder* aBuilder,
331 const nsRect& aClipRect,
332 gfxRect* aDestRect)
333 {
334 if (!mBackgroundStyle)
335 return false;
336
337 if (mBackgroundStyle->mImage.mLayers.Length() != 1)
338 return false;
339
340
341 nsPresContext* presContext = mFrame->PresContext();
342 uint32_t flags = aBuilder->GetBackgroundPaintFlags();
343 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
344 const nsStyleImageLayers::Layer &layer = mBackgroundStyle->mImage.mLayers[mLayer];
345
346 if (layer.mAttachment != NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED)
347 return false;
348
349 nsBackgroundLayerState state =
350 nsCSSRendering::PrepareImageLayer(presContext, mFrame, flags,
351 borderArea, aClipRect, layer);
352
353
354 // We only care about images here, not gradients.
355 if (!mIsRasterImage)
356 return false;
357
358 int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
359 *aDestRect = nsLayoutUtils::RectToGfxRect(state.mFillArea, appUnitsPerDevPixel);
360
361 return true;
362 }
363
364
365 void
Paint(nsDisplayListBuilder * aBuilder,nsRenderingContext * aCtx)366 nsDisplayCanvasThemedBackground::Paint(nsDisplayListBuilder* aBuilder,
367 nsRenderingContext* aCtx)
368 {
369 nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
370 nsPoint offset = ToReferenceFrame();
371 nsRect bgClipRect = frame->CanvasArea() + offset;
372
373 PaintInternal(aBuilder, aCtx, mVisibleRect, &bgClipRect);
374 }
375
376 /**
377 * A display item to paint the focus ring for the document.
378 *
379 * The only reason this can't use nsDisplayGeneric is overriding GetBounds.
380 */
381 class nsDisplayCanvasFocus : public nsDisplayItem {
382 public:
nsDisplayCanvasFocus(nsDisplayListBuilder * aBuilder,nsCanvasFrame * aFrame)383 nsDisplayCanvasFocus(nsDisplayListBuilder* aBuilder, nsCanvasFrame *aFrame)
384 : nsDisplayItem(aBuilder, aFrame)
385 {
386 MOZ_COUNT_CTOR(nsDisplayCanvasFocus);
387 }
~nsDisplayCanvasFocus()388 virtual ~nsDisplayCanvasFocus() {
389 MOZ_COUNT_DTOR(nsDisplayCanvasFocus);
390 }
391
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap)392 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
393 bool* aSnap) override
394 {
395 *aSnap = false;
396 // This is an overestimate, but that's not a problem.
397 nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
398 return frame->CanvasArea() + ToReferenceFrame();
399 }
400
Paint(nsDisplayListBuilder * aBuilder,nsRenderingContext * aCtx)401 virtual void Paint(nsDisplayListBuilder* aBuilder,
402 nsRenderingContext* aCtx) override
403 {
404 nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
405 frame->PaintFocus(aCtx->GetDrawTarget(), ToReferenceFrame());
406 }
407
408 NS_DISPLAY_DECL_NAME("CanvasFocus", TYPE_CANVAS_FOCUS)
409 };
410
411 void
BuildDisplayList(nsDisplayListBuilder * aBuilder,const nsRect & aDirtyRect,const nsDisplayListSet & aLists)412 nsCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
413 const nsRect& aDirtyRect,
414 const nsDisplayListSet& aLists)
415 {
416 if (GetPrevInFlow()) {
417 DisplayOverflowContainers(aBuilder, aDirtyRect, aLists);
418 }
419
420 // Force a background to be shown. We may have a background propagated to us,
421 // in which case StyleBackground wouldn't have the right background
422 // and the code in nsFrame::DisplayBorderBackgroundOutline might not give us
423 // a background.
424 // We don't have any border or outline, and our background draws over
425 // the overflow area, so just add nsDisplayCanvasBackground instead of
426 // calling DisplayBorderBackgroundOutline.
427 if (IsVisibleForPainting(aBuilder)) {
428 nsStyleContext* bgSC;
429 const nsStyleBackground* bg = nullptr;
430 bool isThemed = IsThemed();
431 if (!isThemed && nsCSSRendering::FindBackground(this, &bgSC)) {
432 bg = bgSC->StyleBackground();
433 }
434 aLists.BorderBackground()->AppendNewToTop(
435 new (aBuilder) nsDisplayCanvasBackgroundColor(aBuilder, this));
436
437 if (isThemed) {
438 aLists.BorderBackground()->AppendNewToTop(
439 new (aBuilder) nsDisplayCanvasThemedBackground(aBuilder, this));
440 return;
441 }
442
443 if (!bg) {
444 return;
445 }
446
447 const DisplayItemScrollClip* scrollClip =
448 aBuilder->ClipState().GetCurrentInnermostScrollClip();
449
450 bool needBlendContainer = false;
451
452 // Create separate items for each background layer.
453 const nsStyleImageLayers& layers = bg->mImage;
454 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, layers) {
455 if (layers.mLayers[i].mImage.IsEmpty()) {
456 continue;
457 }
458 if (layers.mLayers[i].mBlendMode != NS_STYLE_BLEND_NORMAL) {
459 needBlendContainer = true;
460 }
461
462 nsDisplayList thisItemList;
463 nsDisplayCanvasBackgroundImage* bgItem =
464 new (aBuilder) nsDisplayCanvasBackgroundImage(aBuilder, this, i, bg);
465 if (bgItem->ShouldFixToViewport(aBuilder)) {
466 thisItemList.AppendNewToTop(
467 nsDisplayFixedPosition::CreateForFixedBackground(aBuilder, this, bgItem, i));
468 } else {
469 thisItemList.AppendNewToTop(bgItem);
470 }
471
472 if (layers.mLayers[i].mBlendMode != NS_STYLE_BLEND_NORMAL) {
473 thisItemList.AppendNewToTop(
474 new (aBuilder) nsDisplayBlendMode(aBuilder, this, &thisItemList,
475 layers.mLayers[i].mBlendMode,
476 scrollClip, i + 1));
477 }
478 aLists.BorderBackground()->AppendToTop(&thisItemList);
479 }
480
481 if (needBlendContainer) {
482 aLists.BorderBackground()->AppendNewToTop(
483 nsDisplayBlendContainer::CreateForBackgroundBlendMode(aBuilder, this,
484 aLists.BorderBackground(),
485 scrollClip));
486 }
487 }
488
489 for (nsIFrame* kid : PrincipalChildList()) {
490 // Put our child into its own pseudo-stack.
491 BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
492 }
493
494 #ifdef DEBUG_CANVAS_FOCUS
495 nsCOMPtr<nsIContent> focusContent;
496 aPresContext->EventStateManager()->
497 GetFocusedContent(getter_AddRefs(focusContent));
498
499 bool hasFocus = false;
500 nsCOMPtr<nsISupports> container;
501 aPresContext->GetContainer(getter_AddRefs(container));
502 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
503 if (docShell) {
504 docShell->GetHasFocus(&hasFocus);
505 printf("%p - nsCanvasFrame::Paint R:%d,%d,%d,%d DR: %d,%d,%d,%d\n", this,
506 mRect.x, mRect.y, mRect.width, mRect.height,
507 aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height);
508 }
509 printf("%p - Focus: %s c: %p DoPaint:%s\n", docShell.get(), hasFocus?"Y":"N",
510 focusContent.get(), mDoPaintFocus?"Y":"N");
511 #endif
512
513 if (!mDoPaintFocus)
514 return;
515 // Only paint the focus if we're visible
516 if (!StyleVisibility()->IsVisible())
517 return;
518
519 aLists.Outlines()->AppendNewToTop(new (aBuilder)
520 nsDisplayCanvasFocus(aBuilder, this));
521 }
522
523 void
PaintFocus(DrawTarget * aDrawTarget,nsPoint aPt)524 nsCanvasFrame::PaintFocus(DrawTarget* aDrawTarget, nsPoint aPt)
525 {
526 nsRect focusRect(aPt, GetSize());
527
528 nsIScrollableFrame *scrollableFrame = do_QueryFrame(GetParent());
529 if (scrollableFrame) {
530 nsRect portRect = scrollableFrame->GetScrollPortRect();
531 focusRect.width = portRect.width;
532 focusRect.height = portRect.height;
533 focusRect.MoveBy(scrollableFrame->GetScrollPosition());
534 }
535
536 // XXX use the root frame foreground color, but should we find BODY frame
537 // for HTML documents?
538 nsIFrame* root = mFrames.FirstChild();
539 const nsStyleColor* color = root ? root->StyleColor() : StyleColor();
540 if (!color) {
541 NS_ERROR("current color cannot be found");
542 return;
543 }
544
545 nsCSSRendering::PaintFocus(PresContext(), aDrawTarget,
546 focusRect, color->mColor);
547 }
548
549 /* virtual */ nscoord
GetMinISize(nsRenderingContext * aRenderingContext)550 nsCanvasFrame::GetMinISize(nsRenderingContext *aRenderingContext)
551 {
552 nscoord result;
553 DISPLAY_MIN_WIDTH(this, result);
554 if (mFrames.IsEmpty())
555 result = 0;
556 else
557 result = mFrames.FirstChild()->GetMinISize(aRenderingContext);
558 return result;
559 }
560
561 /* virtual */ nscoord
GetPrefISize(nsRenderingContext * aRenderingContext)562 nsCanvasFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
563 {
564 nscoord result;
565 DISPLAY_PREF_WIDTH(this, result);
566 if (mFrames.IsEmpty())
567 result = 0;
568 else
569 result = mFrames.FirstChild()->GetPrefISize(aRenderingContext);
570 return result;
571 }
572
573 void
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)574 nsCanvasFrame::Reflow(nsPresContext* aPresContext,
575 ReflowOutput& aDesiredSize,
576 const ReflowInput& aReflowInput,
577 nsReflowStatus& aStatus)
578 {
579 MarkInReflow();
580 DO_GLOBAL_REFLOW_COUNT("nsCanvasFrame");
581 DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
582 NS_FRAME_TRACE_REFLOW_IN("nsCanvasFrame::Reflow");
583
584 // Initialize OUT parameter
585 aStatus = NS_FRAME_COMPLETE;
586
587 nsCanvasFrame* prevCanvasFrame = static_cast<nsCanvasFrame*>
588 (GetPrevInFlow());
589 if (prevCanvasFrame) {
590 AutoFrameListPtr overflow(aPresContext,
591 prevCanvasFrame->StealOverflowFrames());
592 if (overflow) {
593 NS_ASSERTION(overflow->OnlyChild(),
594 "must have doc root as canvas frame's only child");
595 nsContainerFrame::ReparentFrameViewList(*overflow, prevCanvasFrame, this);
596 // Prepend overflow to the our child list. There may already be
597 // children placeholders for fixed-pos elements, which don't get
598 // reflowed but must not be lost until the canvas frame is destroyed.
599 mFrames.InsertFrames(this, nullptr, *overflow);
600 }
601 }
602
603 // Set our size up front, since some parts of reflow depend on it
604 // being already set. Note that the computed height may be
605 // unconstrained; that's ok. Consumers should watch out for that.
606 SetSize(nsSize(aReflowInput.ComputedWidth(), aReflowInput.ComputedHeight()));
607
608 // Reflow our one and only normal child frame. It's either the root
609 // element's frame or a placeholder for that frame, if the root element
610 // is abs-pos or fixed-pos. We may have additional children which
611 // are placeholders for continuations of fixed-pos content, but those
612 // don't need to be reflowed. The normal child is always comes before
613 // the fixed-pos placeholders, because we insert it at the start
614 // of the child list, above.
615 ReflowOutput kidDesiredSize(aReflowInput);
616 if (mFrames.IsEmpty()) {
617 // We have no child frame, so return an empty size
618 aDesiredSize.Width() = aDesiredSize.Height() = 0;
619 } else {
620 nsIFrame* kidFrame = mFrames.FirstChild();
621 bool kidDirty = (kidFrame->GetStateBits() & NS_FRAME_IS_DIRTY) != 0;
622
623 ReflowInput
624 kidReflowInput(aPresContext, aReflowInput, kidFrame,
625 aReflowInput.AvailableSize(kidFrame->GetWritingMode()));
626
627 if (aReflowInput.IsBResizeForWM(kidReflowInput.GetWritingMode()) &&
628 (kidFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
629 // Tell our kid it's being block-dir resized too. Bit of a
630 // hack for framesets.
631 kidReflowInput.SetBResize(true);
632 }
633
634 WritingMode wm = aReflowInput.GetWritingMode();
635 WritingMode kidWM = kidReflowInput.GetWritingMode();
636 nsSize containerSize = aReflowInput.ComputedPhysicalSize();
637
638 LogicalMargin margin = kidReflowInput.ComputedLogicalMargin();
639 LogicalPoint kidPt(kidWM, margin.IStart(kidWM), margin.BStart(kidWM));
640
641 kidReflowInput.ApplyRelativePositioning(&kidPt, containerSize);
642
643 // Reflow the frame
644 ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowInput,
645 kidWM, kidPt, containerSize, 0, aStatus);
646
647 // Complete the reflow and position and size the child frame
648 FinishReflowChild(kidFrame, aPresContext, kidDesiredSize, &kidReflowInput,
649 kidWM, kidPt, containerSize, 0);
650
651 if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) {
652 nsIFrame* nextFrame = kidFrame->GetNextInFlow();
653 NS_ASSERTION(nextFrame || aStatus & NS_FRAME_REFLOW_NEXTINFLOW,
654 "If it's incomplete and has no nif yet, it must flag a nif reflow.");
655 if (!nextFrame) {
656 nextFrame = aPresContext->PresShell()->FrameConstructor()->
657 CreateContinuingFrame(aPresContext, kidFrame, this);
658 SetOverflowFrames(nsFrameList(nextFrame, nextFrame));
659 // Root overflow containers will be normal children of
660 // the canvas frame, but that's ok because there
661 // aren't any other frames we need to isolate them from
662 // during reflow.
663 }
664 if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) {
665 nextFrame->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
666 }
667 }
668
669 // If the child frame was just inserted, then we're responsible for making sure
670 // it repaints
671 if (kidDirty) {
672 // But we have a new child, which will affect our background, so
673 // invalidate our whole rect.
674 // Note: Even though we request to be sized to our child's size, our
675 // scroll frame ensures that we are always the size of the viewport.
676 // Also note: GetPosition() on a CanvasFrame is always going to return
677 // (0, 0). We only want to invalidate GetRect() since Get*OverflowRect()
678 // could also include overflow to our top and left (out of the viewport)
679 // which doesn't need to be painted.
680 nsIFrame* viewport = PresContext()->GetPresShell()->GetRootFrame();
681 viewport->InvalidateFrame();
682 }
683
684 // Return our desired size. Normally it's what we're told, but
685 // sometimes we can be given an unconstrained height (when a window
686 // is sizing-to-content), and we should compute our desired height.
687 LogicalSize finalSize(wm);
688 finalSize.ISize(wm) = aReflowInput.ComputedISize();
689 if (aReflowInput.ComputedBSize() == NS_UNCONSTRAINEDSIZE) {
690 finalSize.BSize(wm) = kidFrame->GetLogicalSize(wm).BSize(wm) +
691 kidReflowInput.ComputedLogicalMargin().BStartEnd(wm);
692 } else {
693 finalSize.BSize(wm) = aReflowInput.ComputedBSize();
694 }
695
696 aDesiredSize.SetSize(wm, finalSize);
697 aDesiredSize.SetOverflowAreasToDesiredBounds();
698 aDesiredSize.mOverflowAreas.UnionWith(
699 kidDesiredSize.mOverflowAreas + kidFrame->GetPosition());
700 }
701
702 if (prevCanvasFrame) {
703 ReflowOverflowContainerChildren(aPresContext, aReflowInput,
704 aDesiredSize.mOverflowAreas, 0,
705 aStatus);
706 }
707
708 FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus);
709
710 NS_FRAME_TRACE_REFLOW_OUT("nsCanvasFrame::Reflow", aStatus);
711 NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
712 }
713
714 nsIAtom*
GetType() const715 nsCanvasFrame::GetType() const
716 {
717 return nsGkAtoms::canvasFrame;
718 }
719
720 nsresult
GetContentForEvent(WidgetEvent * aEvent,nsIContent ** aContent)721 nsCanvasFrame::GetContentForEvent(WidgetEvent* aEvent,
722 nsIContent** aContent)
723 {
724 NS_ENSURE_ARG_POINTER(aContent);
725 nsresult rv = nsFrame::GetContentForEvent(aEvent,
726 aContent);
727 if (NS_FAILED(rv) || !*aContent) {
728 nsIFrame* kid = mFrames.FirstChild();
729 if (kid) {
730 rv = kid->GetContentForEvent(aEvent,
731 aContent);
732 }
733 }
734
735 return rv;
736 }
737
738 #ifdef DEBUG_FRAME_DUMP
739 nsresult
GetFrameName(nsAString & aResult) const740 nsCanvasFrame::GetFrameName(nsAString& aResult) const
741 {
742 return MakeFrameName(NS_LITERAL_STRING("Canvas"), aResult);
743 }
744 #endif
745