1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 /* rendering object that goes directly inside the document's scrollbars */
8 
9 #include "nsCanvasFrame.h"
10 
11 #include "gfxContext.h"
12 #include "gfxUtils.h"
13 #include "Layers.h"
14 #include "nsContainerFrame.h"
15 #include "nsContentCreatorFunctions.h"
16 #include "nsCSSRendering.h"
17 #include "nsPresContext.h"
18 #include "nsPopupSetFrame.h"
19 #include "nsGkAtoms.h"
20 #include "nsIFrameInlines.h"
21 #include "nsDisplayList.h"
22 #include "nsCSSFrameConstructor.h"
23 #include "nsFrameManager.h"
24 #include "gfxPlatform.h"
25 #include "nsPrintfCString.h"
26 #include "mozilla/AccessibleCaretEventHub.h"
27 #include "mozilla/BasePrincipal.h"
28 #include "mozilla/ComputedStyle.h"
29 #include "mozilla/StaticPrefs_browser.h"
30 #include "mozilla/dom/AnonymousContent.h"
31 #include "mozilla/layers/StackingContextHelper.h"
32 #include "mozilla/layers/RenderRootStateManager.h"
33 #include "mozilla/PresShell.h"
34 // for focus
35 #include "nsIScrollableFrame.h"
36 #ifdef DEBUG_CANVAS_FOCUS
37 #  include "nsIDocShell.h"
38 #endif
39 
40 //#define DEBUG_CANVAS_FOCUS
41 
42 using namespace mozilla;
43 using namespace mozilla::dom;
44 using namespace mozilla::layout;
45 using namespace mozilla::gfx;
46 using namespace mozilla::layers;
47 
NS_NewCanvasFrame(PresShell * aPresShell,ComputedStyle * aStyle)48 nsCanvasFrame* NS_NewCanvasFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
49   return new (aPresShell) nsCanvasFrame(aStyle, aPresShell->GetPresContext());
50 }
51 
52 NS_IMPL_FRAMEARENA_HELPERS(nsCanvasFrame)
53 
NS_QUERYFRAME_HEAD(nsCanvasFrame)54 NS_QUERYFRAME_HEAD(nsCanvasFrame)
55   NS_QUERYFRAME_ENTRY(nsCanvasFrame)
56   NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
57   NS_QUERYFRAME_ENTRY(nsIPopupContainer)
58 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
59 
60 void nsCanvasFrame::ShowCustomContentContainer() {
61   if (mCustomContentContainer) {
62     mCustomContentContainer->UnsetAttr(kNameSpaceID_None, nsGkAtoms::hidden,
63                                        true);
64   }
65 }
66 
HideCustomContentContainer()67 void nsCanvasFrame::HideCustomContentContainer() {
68   if (mCustomContentContainer) {
69     mCustomContentContainer->SetAttr(kNameSpaceID_None, nsGkAtoms::hidden,
70                                      u"true"_ns, true);
71   }
72 }
73 
CreateAnonymousContent(nsTArray<ContentInfo> & aElements)74 nsresult nsCanvasFrame::CreateAnonymousContent(
75     nsTArray<ContentInfo>& aElements) {
76   MOZ_ASSERT(!mCustomContentContainer);
77 
78   if (!mContent) {
79     return NS_OK;
80   }
81 
82   nsCOMPtr<Document> doc = mContent->OwnerDoc();
83 
84   RefPtr<AccessibleCaretEventHub> eventHub =
85       PresShell()->GetAccessibleCaretEventHub();
86 
87   // This will go through InsertAnonymousContent and such, and we don't really
88   // want it to end up inserting into our content container.
89   //
90   // FIXME(emilio): The fact that this enters into InsertAnonymousContent is a
91   // bit nasty, can we avoid it, maybe doing this off a scriptrunner?
92   if (eventHub) {
93     eventHub->Init();
94   }
95 
96   // Create the custom content container.
97   mCustomContentContainer = doc->CreateHTMLElement(nsGkAtoms::div);
98 #ifdef DEBUG
99   // We restyle our mCustomContentContainer, even though it's root anonymous
100   // content.  Normally that's not OK because the frame constructor doesn't know
101   // how to order the frame tree in such cases, but we make this work for this
102   // particular case, so it's OK.
103   mCustomContentContainer->SetProperty(nsGkAtoms::restylableAnonymousNode,
104                                        reinterpret_cast<void*>(true));
105 #endif  // DEBUG
106 
107   mCustomContentContainer->SetProperty(
108       nsGkAtoms::docLevelNativeAnonymousContent, reinterpret_cast<void*>(true));
109 
110   // This will usually be done by the caller, but in this case we do it here,
111   // since we reuse the document's AnoymousContent list, and those survive
112   // across reframes and thus may already be flagged as being in an anonymous
113   // subtree. We don't really want to have this semi-broken state where
114   // anonymous nodes have a non-anonymous.
115   mCustomContentContainer->SetIsNativeAnonymousRoot();
116 
117   aElements.AppendElement(mCustomContentContainer);
118 
119   // Do not create an accessible object for the container.
120   mCustomContentContainer->SetAttr(kNameSpaceID_None, nsGkAtoms::role,
121                                    u"presentation"_ns, false);
122 
123   mCustomContentContainer->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
124                                    u"moz-custom-content-container"_ns, false);
125 
126   // Only create a frame for mCustomContentContainer if it has some children.
127   if (doc->GetAnonymousContents().IsEmpty()) {
128     HideCustomContentContainer();
129   }
130 
131   for (RefPtr<AnonymousContent>& anonContent : doc->GetAnonymousContents()) {
132     if (nsCOMPtr<nsINode> parent = anonContent->ContentNode().GetParentNode()) {
133       // Parent had better be an old custom content container already removed
134       // from a reframe. Forget about it since we're about to get inserted in a
135       // new one.
136       //
137       // TODO(emilio): Maybe we should extend PostDestroyData and do this stuff
138       // there instead, or something...
139       MOZ_ASSERT(parent != mCustomContentContainer);
140       MOZ_ASSERT(parent->IsElement());
141       MOZ_ASSERT(parent->AsElement()->IsRootOfNativeAnonymousSubtree());
142       MOZ_ASSERT(!parent->IsInComposedDoc());
143       MOZ_ASSERT(!parent->GetParentNode());
144 
145       parent->RemoveChildNode(&anonContent->ContentNode(), false);
146     }
147 
148     mCustomContentContainer->AppendChildTo(&anonContent->ContentNode(), false,
149                                            IgnoreErrors());
150   }
151 
152   // Create a popupgroup element for system privileged non-XUL documents to
153   // support context menus and tooltips.
154   if (XRE_IsParentProcess() && doc->NodePrincipal()->IsSystemPrincipal()) {
155     nsNodeInfoManager* nodeInfoManager = doc->NodeInfoManager();
156     RefPtr<NodeInfo> nodeInfo =
157         nodeInfoManager->GetNodeInfo(nsGkAtoms::popupgroup, nullptr,
158                                      kNameSpaceID_XUL, nsINode::ELEMENT_NODE);
159 
160     nsresult rv = NS_NewXULElement(getter_AddRefs(mPopupgroupContent),
161                                    nodeInfo.forget(), dom::NOT_FROM_PARSER);
162     NS_ENSURE_SUCCESS(rv, rv);
163 
164     mPopupgroupContent->SetProperty(nsGkAtoms::docLevelNativeAnonymousContent,
165                                     reinterpret_cast<void*>(true));
166 
167     aElements.AppendElement(mPopupgroupContent);
168 
169     nodeInfo = nodeInfoManager->GetNodeInfo(
170         nsGkAtoms::tooltip, nullptr, kNameSpaceID_XUL, nsINode::ELEMENT_NODE);
171 
172     rv = NS_NewXULElement(getter_AddRefs(mTooltipContent), nodeInfo.forget(),
173                           dom::NOT_FROM_PARSER);
174     NS_ENSURE_SUCCESS(rv, rv);
175 
176     mTooltipContent->SetAttr(kNameSpaceID_None, nsGkAtoms::_default, u"true"_ns,
177                              false);
178     // Set the page attribute so XULTooltipElement::PostHandleEvent will find
179     // the text for the tooltip from the currently hovered element.
180     mTooltipContent->SetAttr(kNameSpaceID_None, nsGkAtoms::page, u"true"_ns,
181                              false);
182 
183     mTooltipContent->SetProperty(nsGkAtoms::docLevelNativeAnonymousContent,
184                                  reinterpret_cast<void*>(true));
185 
186     aElements.AppendElement(mTooltipContent);
187   }
188 
189 #ifdef DEBUG
190   for (auto& element : aElements) {
191     MOZ_ASSERT(element.mContent->GetProperty(
192                    nsGkAtoms::docLevelNativeAnonymousContent),
193                "NAC from the canvas frame needs to be document-level, otherwise"
194                " it (1) inherits from the document which is unexpected, and (2)"
195                " StyleChildrenIterator won't be able to find it properly");
196   }
197 #endif
198   return NS_OK;
199 }
200 
AppendAnonymousContentTo(nsTArray<nsIContent * > & aElements,uint32_t aFilter)201 void nsCanvasFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
202                                              uint32_t aFilter) {
203   if (mCustomContentContainer) {
204     aElements.AppendElement(mCustomContentContainer);
205   }
206   if (mPopupgroupContent) {
207     aElements.AppendElement(mPopupgroupContent);
208   }
209   if (mTooltipContent) {
210     aElements.AppendElement(mTooltipContent);
211   }
212 }
213 
DestroyFrom(nsIFrame * aDestructRoot,PostDestroyData & aPostDestroyData)214 void nsCanvasFrame::DestroyFrom(nsIFrame* aDestructRoot,
215                                 PostDestroyData& aPostDestroyData) {
216   nsIScrollableFrame* sf =
217       PresContext()->GetPresShell()->GetRootScrollFrameAsScrollable();
218   if (sf) {
219     sf->RemoveScrollPositionListener(this);
220   }
221 
222   aPostDestroyData.AddAnonymousContent(mCustomContentContainer.forget());
223   if (mPopupgroupContent) {
224     aPostDestroyData.AddAnonymousContent(mPopupgroupContent.forget());
225   }
226   if (mTooltipContent) {
227     aPostDestroyData.AddAnonymousContent(mTooltipContent.forget());
228   }
229 
230   MOZ_ASSERT(!mPopupSetFrame ||
231                  nsLayoutUtils::IsProperAncestorFrame(this, mPopupSetFrame),
232              "Someone forgot to clear popup set frame");
233   nsContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
234 }
235 
ScrollPositionWillChange(nscoord aX,nscoord aY)236 void nsCanvasFrame::ScrollPositionWillChange(nscoord aX, nscoord aY) {
237   if (mDoPaintFocus) {
238     mDoPaintFocus = false;
239     PresShell()->GetRootFrame()->InvalidateFrameSubtree();
240   }
241 }
242 
243 NS_IMETHODIMP
SetHasFocus(bool aHasFocus)244 nsCanvasFrame::SetHasFocus(bool aHasFocus) {
245   if (mDoPaintFocus != aHasFocus) {
246     mDoPaintFocus = aHasFocus;
247     PresShell()->GetRootFrame()->InvalidateFrameSubtree();
248 
249     if (!mAddedScrollPositionListener) {
250       nsIScrollableFrame* sf =
251           PresContext()->GetPresShell()->GetRootScrollFrameAsScrollable();
252       if (sf) {
253         sf->AddScrollPositionListener(this);
254         mAddedScrollPositionListener = true;
255       }
256     }
257   }
258   return NS_OK;
259 }
260 
SetInitialChildList(ChildListID aListID,nsFrameList & aChildList)261 void nsCanvasFrame::SetInitialChildList(ChildListID aListID,
262                                         nsFrameList& aChildList) {
263   NS_ASSERTION(aListID != kPrincipalList || aChildList.IsEmpty() ||
264                    aChildList.OnlyChild(),
265                "Primary child list can have at most one frame in it");
266   nsContainerFrame::SetInitialChildList(aListID, aChildList);
267 }
268 
AppendFrames(ChildListID aListID,nsFrameList & aFrameList)269 void nsCanvasFrame::AppendFrames(ChildListID aListID, nsFrameList& aFrameList) {
270 #ifdef DEBUG
271   MOZ_ASSERT(aListID == kPrincipalList, "unexpected child list");
272   if (!mFrames.IsEmpty()) {
273     for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
274       // We only allow native anonymous child frames to be in principal child
275       // list in canvas frame.
276       MOZ_ASSERT(e.get()->GetContent()->IsInNativeAnonymousSubtree(),
277                  "invalid child list");
278     }
279   }
280   nsIFrame::VerifyDirtyBitSet(aFrameList);
281 #endif
282   nsContainerFrame::AppendFrames(aListID, aFrameList);
283 }
284 
InsertFrames(ChildListID aListID,nsIFrame * aPrevFrame,const nsLineList::iterator * aPrevFrameLine,nsFrameList & aFrameList)285 void nsCanvasFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
286                                  const nsLineList::iterator* aPrevFrameLine,
287                                  nsFrameList& aFrameList) {
288   // Because we only support a single child frame inserting is the same
289   // as appending
290   MOZ_ASSERT(!aPrevFrame, "unexpected previous sibling frame");
291   AppendFrames(aListID, aFrameList);
292 }
293 
294 #ifdef DEBUG
RemoveFrame(ChildListID aListID,nsIFrame * aOldFrame)295 void nsCanvasFrame::RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) {
296   MOZ_ASSERT(aListID == kPrincipalList, "unexpected child list");
297   nsContainerFrame::RemoveFrame(aListID, aOldFrame);
298 }
299 #endif
300 
CanvasArea() const301 nsRect nsCanvasFrame::CanvasArea() const {
302   // Not clear which overflow rect we want here, but it probably doesn't
303   // matter.
304   nsRect result(InkOverflowRect());
305 
306   nsIScrollableFrame* scrollableFrame = do_QueryFrame(GetParent());
307   if (scrollableFrame) {
308     nsRect portRect = scrollableFrame->GetScrollPortRect();
309     result.UnionRect(result, nsRect(nsPoint(0, 0), portRect.Size()));
310   }
311   return result;
312 }
313 
GetPopupSetFrame()314 nsPopupSetFrame* nsCanvasFrame::GetPopupSetFrame() { return mPopupSetFrame; }
315 
SetPopupSetFrame(nsPopupSetFrame * aPopupSet)316 void nsCanvasFrame::SetPopupSetFrame(nsPopupSetFrame* aPopupSet) {
317   MOZ_ASSERT(!aPopupSet || !mPopupSetFrame,
318              "Popup set is already defined! Only 1 allowed.");
319   mPopupSetFrame = aPopupSet;
320 }
321 
GetDefaultTooltip()322 Element* nsCanvasFrame::GetDefaultTooltip() { return mTooltipContent; }
323 
SetDefaultTooltip(Element * aTooltip)324 void nsCanvasFrame::SetDefaultTooltip(Element* aTooltip) {
325   MOZ_ASSERT(!aTooltip || aTooltip == mTooltipContent,
326              "Default tooltip should be anonymous content tooltip.");
327   mTooltipContent = aTooltip;
328 }
329 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)330 void nsDisplayCanvasBackgroundColor::Paint(nsDisplayListBuilder* aBuilder,
331                                            gfxContext* aCtx) {
332   nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
333   nsPoint offset = ToReferenceFrame();
334   nsRect bgClipRect = frame->CanvasArea() + offset;
335   if (NS_GET_A(mColor) > 0) {
336     DrawTarget* drawTarget = aCtx->GetDrawTarget();
337     int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
338     Rect devPxRect =
339         NSRectToSnappedRect(bgClipRect, appUnitsPerDevPixel, *drawTarget);
340     drawTarget->FillRect(devPxRect, ColorPattern(ToDeviceColor(mColor)));
341   }
342 }
343 
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)344 already_AddRefed<Layer> nsDisplayCanvasBackgroundColor::BuildLayer(
345     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
346     const ContainerLayerParameters& aContainerParameters) {
347   if (NS_GET_A(mColor) == 0) {
348     return nullptr;
349   }
350 
351   RefPtr<ColorLayer> layer = static_cast<ColorLayer*>(
352       aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
353   if (!layer) {
354     layer = aManager->CreateColorLayer();
355     if (!layer) {
356       return nullptr;
357     }
358   }
359   layer->SetColor(ToDeviceColor(mColor));
360 
361   nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
362   nsPoint offset = ToReferenceFrame();
363   nsRect bgClipRect = frame->CanvasArea() + offset;
364 
365   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
366 
367   layer->SetBounds(bgClipRect.ToNearestPixels(appUnitsPerDevPixel));
368   layer->SetBaseTransform(gfx::Matrix4x4::Translation(
369       aContainerParameters.mOffset.x, aContainerParameters.mOffset.y, 0));
370 
371   return layer.forget();
372 }
373 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)374 bool nsDisplayCanvasBackgroundColor::CreateWebRenderCommands(
375     mozilla::wr::DisplayListBuilder& aBuilder,
376     mozilla::wr::IpcResourceUpdateQueue& aResources,
377     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
378     nsDisplayListBuilder* aDisplayListBuilder) {
379   ContainerLayerParameters parameter;
380 
381   nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
382   nsPoint offset = ToReferenceFrame();
383   nsRect bgClipRect = frame->CanvasArea() + offset;
384   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
385 
386   LayoutDeviceRect rect =
387       LayoutDeviceRect::FromAppUnits(bgClipRect, appUnitsPerDevPixel);
388 
389   wr::LayoutRect r = wr::ToLayoutRect(rect);
390   aBuilder.PushRect(r, r, !BackfaceIsHidden(),
391                     wr::ToColorF(ToDeviceColor(mColor)));
392   return true;
393 }
394 
WriteDebugInfo(std::stringstream & aStream)395 void nsDisplayCanvasBackgroundColor::WriteDebugInfo(
396     std::stringstream& aStream) {
397   aStream << " (rgba " << (int)NS_GET_R(mColor) << "," << (int)NS_GET_G(mColor)
398           << "," << (int)NS_GET_B(mColor) << "," << (int)NS_GET_A(mColor)
399           << ")";
400 }
401 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)402 void nsDisplayCanvasBackgroundImage::Paint(nsDisplayListBuilder* aBuilder,
403                                            gfxContext* aCtx) {
404   nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
405   nsPoint offset = ToReferenceFrame();
406   nsRect bgClipRect = frame->CanvasArea() + offset;
407 
408   PaintInternal(aBuilder, aCtx, GetPaintRect(), &bgClipRect);
409 }
410 
IsSingleFixedPositionImage(nsDisplayListBuilder * aBuilder,const nsRect & aClipRect,gfxRect * aDestRect)411 bool nsDisplayCanvasBackgroundImage::IsSingleFixedPositionImage(
412     nsDisplayListBuilder* aBuilder, const nsRect& aClipRect,
413     gfxRect* aDestRect) {
414   if (!mBackgroundStyle) return false;
415 
416   if (mBackgroundStyle->StyleBackground()->mImage.mLayers.Length() != 1)
417     return false;
418 
419   nsPresContext* presContext = mFrame->PresContext();
420   uint32_t flags = aBuilder->GetBackgroundPaintFlags();
421   nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
422   const nsStyleImageLayers::Layer& layer =
423       mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
424 
425   if (layer.mAttachment != StyleImageLayerAttachment::Fixed) return false;
426 
427   nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
428       presContext, mFrame, flags, borderArea, aClipRect, layer);
429 
430   // We only care about images here, not gradients.
431   if (!mIsRasterImage) return false;
432 
433   int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
434   *aDestRect =
435       nsLayoutUtils::RectToGfxRect(state.mFillArea, appUnitsPerDevPixel);
436 
437   return true;
438 }
439 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)440 void nsDisplayCanvasThemedBackground::Paint(nsDisplayListBuilder* aBuilder,
441                                             gfxContext* aCtx) {
442   nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
443   nsPoint offset = ToReferenceFrame();
444   nsRect bgClipRect = frame->CanvasArea() + offset;
445 
446   PaintInternal(aBuilder, aCtx, GetPaintRect(), &bgClipRect);
447 }
448 
449 /**
450  * A display item to paint the focus ring for the document.
451  *
452  * The only reason this can't use nsDisplayGeneric is overriding GetBounds.
453  */
454 class nsDisplayCanvasFocus : public nsPaintedDisplayItem {
455  public:
nsDisplayCanvasFocus(nsDisplayListBuilder * aBuilder,nsCanvasFrame * aFrame)456   nsDisplayCanvasFocus(nsDisplayListBuilder* aBuilder, nsCanvasFrame* aFrame)
457       : nsPaintedDisplayItem(aBuilder, aFrame) {
458     MOZ_COUNT_CTOR(nsDisplayCanvasFocus);
459   }
MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayCanvasFocus)460   MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayCanvasFocus)
461 
462   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
463                            bool* aSnap) const override {
464     *aSnap = false;
465     // This is an overestimate, but that's not a problem.
466     nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
467     return frame->CanvasArea() + ToReferenceFrame();
468   }
469 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)470   virtual void Paint(nsDisplayListBuilder* aBuilder,
471                      gfxContext* aCtx) override {
472     nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
473     frame->PaintFocus(aCtx->GetDrawTarget(), ToReferenceFrame());
474   }
475 
476   NS_DISPLAY_DECL_NAME("CanvasFocus", TYPE_CANVAS_FOCUS)
477 };
478 
BuildDisplayList(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)479 void nsCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
480                                      const nsDisplayListSet& aLists) {
481   if (GetPrevInFlow()) {
482     DisplayOverflowContainers(aBuilder, aLists);
483   }
484 
485   // Force a background to be shown. We may have a background propagated to us,
486   // in which case StyleBackground wouldn't have the right background
487   // and the code in nsIFrame::DisplayBorderBackgroundOutline might not give us
488   // a background.
489   // We don't have any border or outline, and our background draws over
490   // the overflow area, so just add nsDisplayCanvasBackground instead of
491   // calling DisplayBorderBackgroundOutline.
492   if (IsVisibleForPainting()) {
493     ComputedStyle* bg = nullptr;
494     nsIFrame* dependentFrame = nullptr;
495     bool isThemed = IsThemed();
496     if (!isThemed &&
497         nsCSSRendering::FindBackgroundFrame(this, &dependentFrame)) {
498       bg = dependentFrame->Style();
499       if (dependentFrame == this) {
500         dependentFrame = nullptr;
501       }
502     }
503 
504     if (isThemed) {
505       aLists.BorderBackground()
506           ->AppendNewToTop<nsDisplayCanvasThemedBackground>(aBuilder, this);
507       return;
508     }
509 
510     if (!bg) {
511       return;
512     }
513 
514     const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
515 
516     bool needBlendContainer = false;
517     nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
518 
519     const bool suppressBackgroundImage = [&] {
520       // Handle print settings.
521       if (!ComputeShouldPaintBackground().mImage) {
522         return true;
523       }
524       // In high-contrast-mode, we suppress background-image on the canvas frame
525       // (even when backplating), because users expect site backgrounds to
526       // conform to their HCM background color when a solid color is rendered,
527       // and some websites use solid-color images instead of an overwritable
528       // background color.
529       if (!PresContext()->PrefSheetPrefs().mUseDocumentColors &&
530           StaticPrefs::
531               browser_display_suppress_canvas_background_image_on_forced_colors()) {
532         return true;
533       }
534       return false;
535     }();
536 
537     // Create separate items for each background layer.
538     const nsStyleImageLayers& layers = bg->StyleBackground()->mImage;
539     NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, layers) {
540       if (layers.mLayers[i].mImage.IsNone() || suppressBackgroundImage) {
541         continue;
542       }
543       if (layers.mLayers[i].mBlendMode != StyleBlend::Normal) {
544         needBlendContainer = true;
545       }
546 
547       nsRect bgRect =
548           GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this);
549 
550       const ActiveScrolledRoot* thisItemASR = asr;
551       nsDisplayList thisItemList;
552       nsDisplayBackgroundImage::InitData bgData =
553           nsDisplayBackgroundImage::GetInitData(aBuilder, this, i, bgRect, bg);
554 
555       if (bgData.shouldFixToViewport) {
556         auto* displayData = aBuilder->GetCurrentFixedBackgroundDisplayData();
557         nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
558             aBuilder, this, aBuilder->GetVisibleRect(),
559             aBuilder->GetDirtyRect());
560 
561         DisplayListClipState::AutoSaveRestore clipState(aBuilder);
562         nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(
563             aBuilder);
564         if (displayData) {
565           nsPoint offset =
566               GetOffsetTo(PresContext()->GetPresShell()->GetRootFrame());
567           aBuilder->SetVisibleRect(displayData->mVisibleRect + offset);
568           aBuilder->SetDirtyRect(displayData->mDirtyRect + offset);
569 
570           clipState.SetClipChainForContainingBlockDescendants(
571               displayData->mContainingBlockClipChain);
572           asrSetter.SetCurrentActiveScrolledRoot(
573               displayData->mContainingBlockActiveScrolledRoot);
574           thisItemASR = displayData->mContainingBlockActiveScrolledRoot;
575         }
576         nsDisplayCanvasBackgroundImage* bgItem = nullptr;
577         {
578           DisplayListClipState::AutoSaveRestore bgImageClip(aBuilder);
579           bgImageClip.Clear();
580           bgItem = MakeDisplayItemWithIndex<nsDisplayCanvasBackgroundImage>(
581               aBuilder, this, /* aIndex = */ i, bgData);
582           if (bgItem) {
583             bgItem->SetDependentFrame(aBuilder, dependentFrame);
584           }
585         }
586         if (bgItem) {
587           thisItemList.AppendToTop(
588               nsDisplayFixedPosition::CreateForFixedBackground(
589                   aBuilder, this, nullptr, bgItem, i));
590         }
591 
592       } else {
593         nsDisplayCanvasBackgroundImage* bgItem =
594             MakeDisplayItemWithIndex<nsDisplayCanvasBackgroundImage>(
595                 aBuilder, this, /* aIndex = */ i, bgData);
596         if (bgItem) {
597           bgItem->SetDependentFrame(aBuilder, dependentFrame);
598           thisItemList.AppendToTop(bgItem);
599         }
600       }
601 
602       if (layers.mLayers[i].mBlendMode != StyleBlend::Normal) {
603         DisplayListClipState::AutoSaveRestore blendClip(aBuilder);
604         thisItemList.AppendNewToTopWithIndex<nsDisplayBlendMode>(
605             aBuilder, this, i + 1, &thisItemList, layers.mLayers[i].mBlendMode,
606             thisItemASR, true);
607       }
608       aLists.BorderBackground()->AppendToTop(&thisItemList);
609     }
610 
611     bool hasFixedBottomLayer =
612         layers.mImageCount > 0 &&
613         layers.mLayers[0].mAttachment == StyleImageLayerAttachment::Fixed;
614 
615     if (!hasFixedBottomLayer || needBlendContainer) {
616       // Put a scrolled background color item in place, at the bottom of the
617       // list. The color of this item will be filled in during
618       // PresShell::AddCanvasBackgroundColorItem.
619       // Do not add this item if there's a fixed background image at the bottom
620       // (unless we have to, for correct blending); with a fixed background,
621       // it's better to allow the fixed background image to combine itself with
622       // a non-scrolled background color directly underneath, rather than
623       // interleaving the two with a scrolled background color.
624       // PresShell::AddCanvasBackgroundColorItem makes sure there always is a
625       // non-scrolled background color item at the bottom.
626       aLists.BorderBackground()
627           ->AppendNewToBottom<nsDisplayCanvasBackgroundColor>(aBuilder, this);
628     }
629 
630     if (needBlendContainer) {
631       const ActiveScrolledRoot* containerASR = contASRTracker.GetContainerASR();
632       DisplayListClipState::AutoSaveRestore blendContainerClip(aBuilder);
633       aLists.BorderBackground()->AppendToTop(
634           nsDisplayBlendContainer::CreateForBackgroundBlendMode(
635               aBuilder, this, nullptr, aLists.BorderBackground(),
636               containerASR));
637     }
638   }
639 
640   for (nsIFrame* kid : PrincipalChildList()) {
641     // Put our child into its own pseudo-stack.
642     BuildDisplayListForChild(aBuilder, kid, aLists);
643   }
644 
645 #ifdef DEBUG_CANVAS_FOCUS
646   nsCOMPtr<nsIContent> focusContent;
647   aPresContext->EventStateManager()->GetFocusedContent(
648       getter_AddRefs(focusContent));
649 
650   bool hasFocus = false;
651   nsCOMPtr<nsISupports> container;
652   aPresContext->GetContainer(getter_AddRefs(container));
653   nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
654   if (docShell) {
655     docShell->GetHasFocus(&hasFocus);
656     nsRect dirty = aBuilder->GetDirtyRect();
657     printf("%p - nsCanvasFrame::Paint R:%d,%d,%d,%d  DR: %d,%d,%d,%d\n", this,
658            mRect.x, mRect.y, mRect.width, mRect.height, dirty.x, dirty.y,
659            dirty.width, dirty.height);
660   }
661   printf("%p - Focus: %s   c: %p  DoPaint:%s\n", docShell.get(),
662          hasFocus ? "Y" : "N", focusContent.get(), mDoPaintFocus ? "Y" : "N");
663 #endif
664 
665   if (!mDoPaintFocus) return;
666   // Only paint the focus if we're visible
667   if (!StyleVisibility()->IsVisible()) return;
668 
669   aLists.Outlines()->AppendNewToTop<nsDisplayCanvasFocus>(aBuilder, this);
670 }
671 
PaintFocus(DrawTarget * aDrawTarget,nsPoint aPt)672 void nsCanvasFrame::PaintFocus(DrawTarget* aDrawTarget, nsPoint aPt) {
673   nsRect focusRect(aPt, GetSize());
674 
675   nsIScrollableFrame* scrollableFrame = do_QueryFrame(GetParent());
676   if (scrollableFrame) {
677     nsRect portRect = scrollableFrame->GetScrollPortRect();
678     focusRect.width = portRect.width;
679     focusRect.height = portRect.height;
680     focusRect.MoveBy(scrollableFrame->GetScrollPosition());
681   }
682 
683   // XXX use the root frame foreground color, but should we find BODY frame
684   // for HTML documents?
685   nsIFrame* root = mFrames.FirstChild();
686   const auto* text = root ? root->StyleText() : StyleText();
687   nsCSSRendering::PaintFocus(PresContext(), aDrawTarget, focusRect,
688                              text->mColor.ToColor());
689 }
690 
691 /* virtual */
GetMinISize(gfxContext * aRenderingContext)692 nscoord nsCanvasFrame::GetMinISize(gfxContext* aRenderingContext) {
693   nscoord result;
694   DISPLAY_MIN_INLINE_SIZE(this, result);
695   if (mFrames.IsEmpty())
696     result = 0;
697   else
698     result = mFrames.FirstChild()->GetMinISize(aRenderingContext);
699   return result;
700 }
701 
702 /* virtual */
GetPrefISize(gfxContext * aRenderingContext)703 nscoord nsCanvasFrame::GetPrefISize(gfxContext* aRenderingContext) {
704   nscoord result;
705   DISPLAY_PREF_INLINE_SIZE(this, result);
706   if (mFrames.IsEmpty())
707     result = 0;
708   else
709     result = mFrames.FirstChild()->GetPrefISize(aRenderingContext);
710   return result;
711 }
712 
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)713 void nsCanvasFrame::Reflow(nsPresContext* aPresContext,
714                            ReflowOutput& aDesiredSize,
715                            const ReflowInput& aReflowInput,
716                            nsReflowStatus& aStatus) {
717   MarkInReflow();
718   DO_GLOBAL_REFLOW_COUNT("nsCanvasFrame");
719   DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
720   MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
721   NS_FRAME_TRACE_REFLOW_IN("nsCanvasFrame::Reflow");
722 
723   nsCanvasFrame* prevCanvasFrame = static_cast<nsCanvasFrame*>(GetPrevInFlow());
724   if (prevCanvasFrame) {
725     AutoFrameListPtr overflow(aPresContext,
726                               prevCanvasFrame->StealOverflowFrames());
727     if (overflow) {
728       NS_ASSERTION(overflow->OnlyChild(),
729                    "must have doc root as canvas frame's only child");
730       nsContainerFrame::ReparentFrameViewList(*overflow, prevCanvasFrame, this);
731       // Prepend overflow to the our child list. There may already be
732       // children placeholders for fixed-pos elements, which don't get
733       // reflowed but must not be lost until the canvas frame is destroyed.
734       mFrames.InsertFrames(this, nullptr, *overflow);
735     }
736   }
737 
738   // Set our size up front, since some parts of reflow depend on it
739   // being already set.  Note that the computed height may be
740   // unconstrained; that's ok.  Consumers should watch out for that.
741   SetSize(nsSize(aReflowInput.ComputedWidth(), aReflowInput.ComputedHeight()));
742 
743   // Reflow our children.  Typically, we only have one child - the root
744   // element's frame or a placeholder for that frame, if the root element
745   // is abs-pos or fixed-pos.  Note that this child might be missing though
746   // if that frame was Complete in one of our earlier continuations.  This
747   // happens when we create additional pages purely to make room for painting
748   // overflow (painted by BuildPreviousPageOverflow in nsPageFrame.cpp).
749   // We may have additional children which are placeholders for continuations
750   // of fixed-pos content, see nsCSSFrameConstructor::ReplicateFixedFrames.
751   // We may also have a nsPopupSetFrame child (mPopupSetFrame).
752   const WritingMode wm = aReflowInput.GetWritingMode();
753   aDesiredSize.SetSize(wm, aReflowInput.ComputedSize());
754   if (aReflowInput.ComputedBSize() == NS_UNCONSTRAINEDSIZE) {
755     // Set the block-size to zero for now in case we don't have any non-
756     // placeholder children that would update the size in the loop below.
757     aDesiredSize.BSize(wm) = nscoord(0);
758   }
759   aDesiredSize.SetOverflowAreasToDesiredBounds();
760   nsIFrame* nextKid = nullptr;
761   for (auto* kidFrame = mFrames.FirstChild(); kidFrame; kidFrame = nextKid) {
762     nextKid = kidFrame->GetNextSibling();
763     if (kidFrame == mPopupSetFrame) {
764       // This child is handled separately after this loop.
765       continue;
766     }
767 
768     ReflowOutput kidDesiredSize(aReflowInput);
769     bool kidDirty = kidFrame->HasAnyStateBits(NS_FRAME_IS_DIRTY);
770     WritingMode kidWM = kidFrame->GetWritingMode();
771     auto availableSize = aReflowInput.AvailableSize(kidWM);
772     nscoord bOffset = 0;
773     nscoord canvasBSizeSum = 0;
774     if (prevCanvasFrame && availableSize.BSize(kidWM) != NS_UNCONSTRAINEDSIZE &&
775         !kidFrame->IsPlaceholderFrame() &&
776         StaticPrefs::layout_display_list_improve_fragmentation()) {
777       for (auto* pif = prevCanvasFrame; pif;
778            pif = static_cast<nsCanvasFrame*>(pif->GetPrevInFlow())) {
779         canvasBSizeSum += pif->BSize(kidWM);
780         auto* pifChild = pif->PrincipalChildList().FirstChild();
781         if (pifChild) {
782           nscoord layoutOverflow = pifChild->BSize(kidWM) - canvasBSizeSum;
783           // A negative value means that the :root frame does not fill
784           // the canvas.  In this case we can't determine the offset exactly
785           // so we use the end edge of the scrollable overflow as the offset
786           // instead.  This will likely push down the content below where it
787           // should be placed, creating a gap.  That's preferred over making
788           // content overlap which would otherwise occur.
789           // See layout/reftests/pagination/inline-block-slice-7.html for an
790           // example of this.
791           if (layoutOverflow < 0) {
792             LogicalRect so(kidWM, pifChild->ScrollableOverflowRect(),
793                            pifChild->GetSize());
794             layoutOverflow = so.BEnd(kidWM) - canvasBSizeSum;
795           }
796           bOffset = std::max(bOffset, layoutOverflow);
797         }
798       }
799       availableSize.BSize(kidWM) -= bOffset;
800     }
801 
802     if (MOZ_LIKELY(availableSize.BSize(kidWM) > 0)) {
803       ReflowInput kidReflowInput(aPresContext, aReflowInput, kidFrame,
804                                  availableSize);
805 
806       if (aReflowInput.IsBResizeForWM(kidReflowInput.GetWritingMode()) &&
807           kidFrame->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
808         // Tell our kid it's being block-dir resized too.  Bit of a
809         // hack for framesets.
810         kidReflowInput.SetBResize(true);
811       }
812 
813       nsSize containerSize = aReflowInput.ComputedPhysicalSize();
814       LogicalMargin margin = kidReflowInput.ComputedLogicalMargin(kidWM);
815       LogicalPoint kidPt(kidWM, margin.IStart(kidWM), margin.BStart(kidWM));
816       (kidWM.IsOrthogonalTo(wm) ? kidPt.I(kidWM) : kidPt.B(kidWM)) += bOffset;
817 
818       nsReflowStatus kidStatus;
819       ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowInput, kidWM,
820                   kidPt, containerSize, ReflowChildFlags::Default, kidStatus);
821 
822       FinishReflowChild(kidFrame, aPresContext, kidDesiredSize, &kidReflowInput,
823                         kidWM, kidPt, containerSize,
824                         ReflowChildFlags::ApplyRelativePositioning);
825 
826       if (!kidStatus.IsFullyComplete()) {
827         nsIFrame* nextFrame = kidFrame->GetNextInFlow();
828         NS_ASSERTION(nextFrame || kidStatus.NextInFlowNeedsReflow(),
829                      "If it's incomplete and has no nif yet, it must flag a "
830                      "nif reflow.");
831         if (!nextFrame) {
832           nextFrame = aPresContext->PresShell()
833                           ->FrameConstructor()
834                           ->CreateContinuingFrame(kidFrame, this);
835           SetOverflowFrames(nsFrameList(nextFrame, nextFrame));
836           // Root overflow containers will be normal children of
837           // the canvas frame, but that's ok because there
838           // aren't any other frames we need to isolate them from
839           // during reflow.
840         }
841         if (kidStatus.IsOverflowIncomplete()) {
842           nextFrame->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
843         }
844       }
845       aStatus.MergeCompletionStatusFrom(kidStatus);
846 
847       // If the child frame was just inserted, then we're responsible for making
848       // sure it repaints
849       if (kidDirty) {
850         // But we have a new child, which will affect our background, so
851         // invalidate our whole rect.
852         // Note: Even though we request to be sized to our child's size, our
853         // scroll frame ensures that we are always the size of the viewport.
854         // Also note: GetPosition() on a CanvasFrame is always going to return
855         // (0, 0). We only want to invalidate GetRect() since Get*OverflowRect()
856         // could also include overflow to our top and left (out of the viewport)
857         // which doesn't need to be painted.
858         nsIFrame* viewport = PresContext()->GetPresShell()->GetRootFrame();
859         viewport->InvalidateFrame();
860       }
861 
862       // Return our desired size. Normally it's what we're told, but
863       // sometimes we can be given an unconstrained block-size (when a window
864       // is sizing-to-content), and we should compute our desired block-size.
865       if (aReflowInput.ComputedBSize() == NS_UNCONSTRAINEDSIZE &&
866           !kidFrame->IsPlaceholderFrame()) {
867         LogicalSize finalSize = aReflowInput.ComputedSize();
868         finalSize.BSize(wm) =
869             kidFrame->GetLogicalSize(wm).BSize(wm) +
870             kidReflowInput.ComputedLogicalMargin(wm).BStartEnd(wm);
871         aDesiredSize.SetSize(wm, finalSize);
872         aDesiredSize.SetOverflowAreasToDesiredBounds();
873       }
874       aDesiredSize.mOverflowAreas.UnionWith(kidDesiredSize.mOverflowAreas +
875                                             kidFrame->GetPosition());
876     } else if (kidFrame->IsPlaceholderFrame()) {
877       // Placeholders always fit even if there's no available block-size left.
878     } else {
879       // This only occurs in paginated mode.  There is no available space on
880       // this page due to reserving space for overflow from a previous page,
881       // so we push our child to the next page.  Note that we can have some
882       // placeholders for fixed pos. frames in mFrames too, so we need to be
883       // careful to only push `kidFrame`.
884       mFrames.RemoveFrame(kidFrame);
885       SetOverflowFrames(nsFrameList(kidFrame, kidFrame));
886       aStatus.SetIncomplete();
887     }
888   }
889 
890   if (prevCanvasFrame) {
891     ReflowOverflowContainerChildren(aPresContext, aReflowInput,
892                                     aDesiredSize.mOverflowAreas,
893                                     ReflowChildFlags::Default, aStatus);
894   }
895 
896   if (mPopupSetFrame) {
897     MOZ_ASSERT(mFrames.ContainsFrame(mPopupSetFrame),
898                "Only normal flow supported.");
899     nsReflowStatus popupStatus;
900     ReflowOutput popupDesiredSize(aReflowInput.GetWritingMode());
901     WritingMode wm = mPopupSetFrame->GetWritingMode();
902     LogicalSize availSize = aReflowInput.ComputedSize(wm);
903     availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
904     ReflowInput popupReflowInput(aPresContext, aReflowInput, mPopupSetFrame,
905                                  availSize);
906     ReflowChild(mPopupSetFrame, aPresContext, popupDesiredSize,
907                 popupReflowInput, 0, 0, ReflowChildFlags::NoMoveFrame,
908                 popupStatus);
909     FinishReflowChild(mPopupSetFrame, aPresContext, popupDesiredSize,
910                       &popupReflowInput, 0, 0, ReflowChildFlags::NoMoveFrame);
911   }
912 
913   FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput,
914                                  aStatus);
915 
916   NS_FRAME_TRACE_REFLOW_OUT("nsCanvasFrame::Reflow", aStatus);
917   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
918 }
919 
GetContentForEvent(WidgetEvent * aEvent,nsIContent ** aContent)920 nsresult nsCanvasFrame::GetContentForEvent(WidgetEvent* aEvent,
921                                            nsIContent** aContent) {
922   NS_ENSURE_ARG_POINTER(aContent);
923   nsresult rv = nsIFrame::GetContentForEvent(aEvent, aContent);
924   if (NS_FAILED(rv) || !*aContent) {
925     nsIFrame* kid = mFrames.FirstChild();
926     if (kid) {
927       rv = kid->GetContentForEvent(aEvent, aContent);
928     }
929   }
930 
931   return rv;
932 }
933 
934 #ifdef DEBUG_FRAME_DUMP
GetFrameName(nsAString & aResult) const935 nsresult nsCanvasFrame::GetFrameName(nsAString& aResult) const {
936   return MakeFrameName(u"Canvas"_ns, aResult);
937 }
938 #endif
939