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