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 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)344 bool nsDisplayCanvasBackgroundColor::CreateWebRenderCommands(
345     mozilla::wr::DisplayListBuilder& aBuilder,
346     mozilla::wr::IpcResourceUpdateQueue& aResources,
347     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
348     nsDisplayListBuilder* aDisplayListBuilder) {
349   nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
350   nsPoint offset = ToReferenceFrame();
351   nsRect bgClipRect = frame->CanvasArea() + offset;
352   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
353 
354   LayoutDeviceRect rect =
355       LayoutDeviceRect::FromAppUnits(bgClipRect, appUnitsPerDevPixel);
356 
357   wr::LayoutRect r = wr::ToLayoutRect(rect);
358   aBuilder.PushRect(r, r, !BackfaceIsHidden(), false,
359                     wr::ToColorF(ToDeviceColor(mColor)));
360   return true;
361 }
362 
WriteDebugInfo(std::stringstream & aStream)363 void nsDisplayCanvasBackgroundColor::WriteDebugInfo(
364     std::stringstream& aStream) {
365   aStream << " (rgba " << (int)NS_GET_R(mColor) << "," << (int)NS_GET_G(mColor)
366           << "," << (int)NS_GET_B(mColor) << "," << (int)NS_GET_A(mColor)
367           << ")";
368 }
369 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)370 void nsDisplayCanvasBackgroundImage::Paint(nsDisplayListBuilder* aBuilder,
371                                            gfxContext* aCtx) {
372   nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
373   nsPoint offset = ToReferenceFrame();
374   nsRect bgClipRect = frame->CanvasArea() + offset;
375 
376   PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), &bgClipRect);
377 }
378 
IsSingleFixedPositionImage(nsDisplayListBuilder * aBuilder,const nsRect & aClipRect,gfxRect * aDestRect)379 bool nsDisplayCanvasBackgroundImage::IsSingleFixedPositionImage(
380     nsDisplayListBuilder* aBuilder, const nsRect& aClipRect,
381     gfxRect* aDestRect) {
382   if (!mBackgroundStyle) return false;
383 
384   if (mBackgroundStyle->StyleBackground()->mImage.mLayers.Length() != 1)
385     return false;
386 
387   nsPresContext* presContext = mFrame->PresContext();
388   uint32_t flags = aBuilder->GetBackgroundPaintFlags();
389   nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
390   const nsStyleImageLayers::Layer& layer =
391       mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
392 
393   if (layer.mAttachment != StyleImageLayerAttachment::Fixed) return false;
394 
395   nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer(
396       presContext, mFrame, flags, borderArea, aClipRect, layer);
397 
398   // We only care about images here, not gradients.
399   if (!mIsRasterImage) return false;
400 
401   int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
402   *aDestRect =
403       nsLayoutUtils::RectToGfxRect(state.mFillArea, appUnitsPerDevPixel);
404 
405   return true;
406 }
407 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)408 void nsDisplayCanvasThemedBackground::Paint(nsDisplayListBuilder* aBuilder,
409                                             gfxContext* aCtx) {
410   nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
411   nsPoint offset = ToReferenceFrame();
412   nsRect bgClipRect = frame->CanvasArea() + offset;
413 
414   PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), &bgClipRect);
415 }
416 
417 /**
418  * A display item to paint the focus ring for the document.
419  *
420  * The only reason this can't use nsDisplayGeneric is overriding GetBounds.
421  */
422 class nsDisplayCanvasFocus : public nsPaintedDisplayItem {
423  public:
nsDisplayCanvasFocus(nsDisplayListBuilder * aBuilder,nsCanvasFrame * aFrame)424   nsDisplayCanvasFocus(nsDisplayListBuilder* aBuilder, nsCanvasFrame* aFrame)
425       : nsPaintedDisplayItem(aBuilder, aFrame) {
426     MOZ_COUNT_CTOR(nsDisplayCanvasFocus);
427   }
MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayCanvasFocus)428   MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayCanvasFocus)
429 
430   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
431                            bool* aSnap) const override {
432     *aSnap = false;
433     // This is an overestimate, but that's not a problem.
434     nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
435     return frame->CanvasArea() + ToReferenceFrame();
436   }
437 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)438   virtual void Paint(nsDisplayListBuilder* aBuilder,
439                      gfxContext* aCtx) override {
440     nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
441     frame->PaintFocus(aCtx->GetDrawTarget(), ToReferenceFrame());
442   }
443 
444   NS_DISPLAY_DECL_NAME("CanvasFocus", TYPE_CANVAS_FOCUS)
445 };
446 
BuildDisplayList(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)447 void nsCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
448                                      const nsDisplayListSet& aLists) {
449   if (GetPrevInFlow()) {
450     DisplayOverflowContainers(aBuilder, aLists);
451   }
452 
453   // Force a background to be shown. We may have a background propagated to us,
454   // in which case StyleBackground wouldn't have the right background
455   // and the code in nsIFrame::DisplayBorderBackgroundOutline might not give us
456   // a background.
457   // We don't have any border or outline, and our background draws over
458   // the overflow area, so just add nsDisplayCanvasBackground instead of
459   // calling DisplayBorderBackgroundOutline.
460   if (IsVisibleForPainting()) {
461     ComputedStyle* bg = nullptr;
462     nsIFrame* dependentFrame = nullptr;
463     bool isThemed = IsThemed();
464     if (!isThemed &&
465         nsCSSRendering::FindBackgroundFrame(this, &dependentFrame)) {
466       bg = dependentFrame->Style();
467       if (dependentFrame == this) {
468         dependentFrame = nullptr;
469       }
470     }
471 
472     if (isThemed) {
473       aLists.BorderBackground()
474           ->AppendNewToTop<nsDisplayCanvasThemedBackground>(aBuilder, this);
475       return;
476     }
477 
478     if (!bg) {
479       return;
480     }
481 
482     const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
483 
484     bool needBlendContainer = false;
485     nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
486 
487     const bool suppressBackgroundImage = [&] {
488       // Handle print settings.
489       if (!ComputeShouldPaintBackground().mImage) {
490         return true;
491       }
492       // In high-contrast-mode, we suppress background-image on the canvas frame
493       // (even when backplating), because users expect site backgrounds to
494       // conform to their HCM background color when a solid color is rendered,
495       // and some websites use solid-color images instead of an overwritable
496       // background color.
497       if (PresContext()->ForcingColors() &&
498           StaticPrefs::
499               browser_display_suppress_canvas_background_image_on_forced_colors()) {
500         return true;
501       }
502       return false;
503     }();
504 
505     nsDisplayList layerItems(aBuilder);
506 
507     // Create separate items for each background layer.
508     const nsStyleImageLayers& layers = bg->StyleBackground()->mImage;
509     NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, layers) {
510       if (layers.mLayers[i].mImage.IsNone() || suppressBackgroundImage) {
511         continue;
512       }
513       if (layers.mLayers[i].mBlendMode != StyleBlend::Normal) {
514         needBlendContainer = true;
515       }
516 
517       nsRect bgRect =
518           GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this);
519 
520       const ActiveScrolledRoot* thisItemASR = asr;
521       nsDisplayList thisItemList(aBuilder);
522       nsDisplayBackgroundImage::InitData bgData =
523           nsDisplayBackgroundImage::GetInitData(aBuilder, this, i, bgRect, bg);
524 
525       if (bgData.shouldFixToViewport) {
526         auto* displayData = aBuilder->GetCurrentFixedBackgroundDisplayData();
527         nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
528             aBuilder, this, aBuilder->GetVisibleRect(),
529             aBuilder->GetDirtyRect());
530 
531         DisplayListClipState::AutoSaveRestore clipState(aBuilder);
532         nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(
533             aBuilder);
534         if (displayData) {
535           nsPoint offset =
536               GetOffsetTo(PresContext()->GetPresShell()->GetRootFrame());
537           aBuilder->SetVisibleRect(displayData->mVisibleRect + offset);
538           aBuilder->SetDirtyRect(displayData->mDirtyRect + offset);
539 
540           clipState.SetClipChainForContainingBlockDescendants(
541               displayData->mContainingBlockClipChain);
542           asrSetter.SetCurrentActiveScrolledRoot(
543               displayData->mContainingBlockActiveScrolledRoot);
544           thisItemASR = displayData->mContainingBlockActiveScrolledRoot;
545         }
546         nsDisplayCanvasBackgroundImage* bgItem = nullptr;
547         {
548           DisplayListClipState::AutoSaveRestore bgImageClip(aBuilder);
549           bgImageClip.Clear();
550           bgItem = MakeDisplayItemWithIndex<nsDisplayCanvasBackgroundImage>(
551               aBuilder, this, /* aIndex = */ i, bgData);
552           if (bgItem) {
553             bgItem->SetDependentFrame(aBuilder, dependentFrame);
554           }
555         }
556         if (bgItem) {
557           thisItemList.AppendToTop(
558               nsDisplayFixedPosition::CreateForFixedBackground(
559                   aBuilder, this, nullptr, bgItem, i, asr));
560         }
561 
562       } else {
563         nsDisplayCanvasBackgroundImage* bgItem =
564             MakeDisplayItemWithIndex<nsDisplayCanvasBackgroundImage>(
565                 aBuilder, this, /* aIndex = */ i, bgData);
566         if (bgItem) {
567           bgItem->SetDependentFrame(aBuilder, dependentFrame);
568           thisItemList.AppendToTop(bgItem);
569         }
570       }
571 
572       if (layers.mLayers[i].mBlendMode != StyleBlend::Normal) {
573         DisplayListClipState::AutoSaveRestore blendClip(aBuilder);
574         thisItemList.AppendNewToTopWithIndex<nsDisplayBlendMode>(
575             aBuilder, this, i + 1, &thisItemList, layers.mLayers[i].mBlendMode,
576             thisItemASR, true);
577       }
578       layerItems.AppendToTop(&thisItemList);
579     }
580 
581     bool hasFixedBottomLayer =
582         layers.mImageCount > 0 &&
583         layers.mLayers[0].mAttachment == StyleImageLayerAttachment::Fixed;
584 
585     if (!hasFixedBottomLayer || needBlendContainer) {
586       // Put a scrolled background color item in place, at the bottom of the
587       // list. The color of this item will be filled in during
588       // PresShell::AddCanvasBackgroundColorItem.
589       // Do not add this item if there's a fixed background image at the bottom
590       // (unless we have to, for correct blending); with a fixed background,
591       // it's better to allow the fixed background image to combine itself with
592       // a non-scrolled background color directly underneath, rather than
593       // interleaving the two with a scrolled background color.
594       // PresShell::AddCanvasBackgroundColorItem makes sure there always is a
595       // non-scrolled background color item at the bottom.
596       aLists.BorderBackground()->AppendNewToTop<nsDisplayCanvasBackgroundColor>(
597           aBuilder, this);
598     }
599 
600     aLists.BorderBackground()->AppendToTop(&layerItems);
601 
602     if (needBlendContainer) {
603       const ActiveScrolledRoot* containerASR = contASRTracker.GetContainerASR();
604       DisplayListClipState::AutoSaveRestore blendContainerClip(aBuilder);
605       aLists.BorderBackground()->AppendToTop(
606           nsDisplayBlendContainer::CreateForBackgroundBlendMode(
607               aBuilder, this, nullptr, aLists.BorderBackground(),
608               containerASR));
609     }
610   }
611 
612   for (nsIFrame* kid : PrincipalChildList()) {
613     // Put our child into its own pseudo-stack.
614     BuildDisplayListForChild(aBuilder, kid, aLists);
615   }
616 
617 #ifdef DEBUG_CANVAS_FOCUS
618   nsCOMPtr<nsIContent> focusContent;
619   aPresContext->EventStateManager()->GetFocusedContent(
620       getter_AddRefs(focusContent));
621 
622   bool hasFocus = false;
623   nsCOMPtr<nsISupports> container;
624   aPresContext->GetContainer(getter_AddRefs(container));
625   nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
626   if (docShell) {
627     docShell->GetHasFocus(&hasFocus);
628     nsRect dirty = aBuilder->GetDirtyRect();
629     printf("%p - nsCanvasFrame::Paint R:%d,%d,%d,%d  DR: %d,%d,%d,%d\n", this,
630            mRect.x, mRect.y, mRect.width, mRect.height, dirty.x, dirty.y,
631            dirty.width, dirty.height);
632   }
633   printf("%p - Focus: %s   c: %p  DoPaint:%s\n", docShell.get(),
634          hasFocus ? "Y" : "N", focusContent.get(), mDoPaintFocus ? "Y" : "N");
635 #endif
636 
637   if (!mDoPaintFocus) return;
638   // Only paint the focus if we're visible
639   if (!StyleVisibility()->IsVisible()) return;
640 
641   aLists.Outlines()->AppendNewToTop<nsDisplayCanvasFocus>(aBuilder, this);
642 }
643 
PaintFocus(DrawTarget * aDrawTarget,nsPoint aPt)644 void nsCanvasFrame::PaintFocus(DrawTarget* aDrawTarget, nsPoint aPt) {
645   nsRect focusRect(aPt, GetSize());
646 
647   nsIScrollableFrame* scrollableFrame = do_QueryFrame(GetParent());
648   if (scrollableFrame) {
649     nsRect portRect = scrollableFrame->GetScrollPortRect();
650     focusRect.width = portRect.width;
651     focusRect.height = portRect.height;
652     focusRect.MoveBy(scrollableFrame->GetScrollPosition());
653   }
654 
655   // XXX use the root frame foreground color, but should we find BODY frame
656   // for HTML documents?
657   nsIFrame* root = mFrames.FirstChild();
658   const auto* text = root ? root->StyleText() : StyleText();
659   nsCSSRendering::PaintFocus(PresContext(), aDrawTarget, focusRect,
660                              text->mColor.ToColor());
661 }
662 
663 /* virtual */
GetMinISize(gfxContext * aRenderingContext)664 nscoord nsCanvasFrame::GetMinISize(gfxContext* aRenderingContext) {
665   nscoord result;
666   DISPLAY_MIN_INLINE_SIZE(this, result);
667   if (mFrames.IsEmpty())
668     result = 0;
669   else
670     result = mFrames.FirstChild()->GetMinISize(aRenderingContext);
671   return result;
672 }
673 
674 /* virtual */
GetPrefISize(gfxContext * aRenderingContext)675 nscoord nsCanvasFrame::GetPrefISize(gfxContext* aRenderingContext) {
676   nscoord result;
677   DISPLAY_PREF_INLINE_SIZE(this, result);
678   if (mFrames.IsEmpty())
679     result = 0;
680   else
681     result = mFrames.FirstChild()->GetPrefISize(aRenderingContext);
682   return result;
683 }
684 
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)685 void nsCanvasFrame::Reflow(nsPresContext* aPresContext,
686                            ReflowOutput& aDesiredSize,
687                            const ReflowInput& aReflowInput,
688                            nsReflowStatus& aStatus) {
689   MarkInReflow();
690   DO_GLOBAL_REFLOW_COUNT("nsCanvasFrame");
691   DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
692   MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
693   NS_FRAME_TRACE_REFLOW_IN("nsCanvasFrame::Reflow");
694 
695   nsCanvasFrame* prevCanvasFrame = static_cast<nsCanvasFrame*>(GetPrevInFlow());
696   if (prevCanvasFrame) {
697     AutoFrameListPtr overflow(aPresContext,
698                               prevCanvasFrame->StealOverflowFrames());
699     if (overflow) {
700       NS_ASSERTION(overflow->OnlyChild(),
701                    "must have doc root as canvas frame's only child");
702       nsContainerFrame::ReparentFrameViewList(*overflow, prevCanvasFrame, this);
703       // Prepend overflow to the our child list. There may already be
704       // children placeholders for fixed-pos elements, which don't get
705       // reflowed but must not be lost until the canvas frame is destroyed.
706       mFrames.InsertFrames(this, nullptr, *overflow);
707     }
708   }
709 
710   // Set our size up front, since some parts of reflow depend on it
711   // being already set.  Note that the computed height may be
712   // unconstrained; that's ok.  Consumers should watch out for that.
713   SetSize(nsSize(aReflowInput.ComputedWidth(), aReflowInput.ComputedHeight()));
714 
715   // Reflow our children.  Typically, we only have one child - the root
716   // element's frame or a placeholder for that frame, if the root element
717   // is abs-pos or fixed-pos.  Note that this child might be missing though
718   // if that frame was Complete in one of our earlier continuations.  This
719   // happens when we create additional pages purely to make room for painting
720   // overflow (painted by BuildPreviousPageOverflow in nsPageFrame.cpp).
721   // We may have additional children which are placeholders for continuations
722   // of fixed-pos content, see nsCSSFrameConstructor::ReplicateFixedFrames.
723   // We may also have a nsPopupSetFrame child (mPopupSetFrame).
724   const WritingMode wm = aReflowInput.GetWritingMode();
725   aDesiredSize.SetSize(wm, aReflowInput.ComputedSize());
726   if (aReflowInput.ComputedBSize() == NS_UNCONSTRAINEDSIZE) {
727     // Set the block-size to zero for now in case we don't have any non-
728     // placeholder children that would update the size in the loop below.
729     aDesiredSize.BSize(wm) = nscoord(0);
730   }
731   aDesiredSize.SetOverflowAreasToDesiredBounds();
732   nsIFrame* nextKid = nullptr;
733   for (auto* kidFrame = mFrames.FirstChild(); kidFrame; kidFrame = nextKid) {
734     nextKid = kidFrame->GetNextSibling();
735     if (kidFrame == mPopupSetFrame) {
736       // This child is handled separately after this loop.
737       continue;
738     }
739 
740     ReflowOutput kidDesiredSize(aReflowInput);
741     bool kidDirty = kidFrame->HasAnyStateBits(NS_FRAME_IS_DIRTY);
742     WritingMode kidWM = kidFrame->GetWritingMode();
743     auto availableSize = aReflowInput.AvailableSize(kidWM);
744     nscoord bOffset = 0;
745     nscoord canvasBSizeSum = 0;
746     if (prevCanvasFrame && availableSize.BSize(kidWM) != NS_UNCONSTRAINEDSIZE &&
747         !kidFrame->IsPlaceholderFrame() &&
748         StaticPrefs::layout_display_list_improve_fragmentation()) {
749       for (auto* pif = prevCanvasFrame; pif;
750            pif = static_cast<nsCanvasFrame*>(pif->GetPrevInFlow())) {
751         canvasBSizeSum += pif->BSize(kidWM);
752         auto* pifChild = pif->PrincipalChildList().FirstChild();
753         if (pifChild) {
754           nscoord layoutOverflow = pifChild->BSize(kidWM) - canvasBSizeSum;
755           // A negative value means that the :root frame does not fill
756           // the canvas.  In this case we can't determine the offset exactly
757           // so we use the end edge of the scrollable overflow as the offset
758           // instead.  This will likely push down the content below where it
759           // should be placed, creating a gap.  That's preferred over making
760           // content overlap which would otherwise occur.
761           // See layout/reftests/pagination/inline-block-slice-7.html for an
762           // example of this.
763           if (layoutOverflow < 0) {
764             LogicalRect so(kidWM, pifChild->ScrollableOverflowRect(),
765                            pifChild->GetSize());
766             layoutOverflow = so.BEnd(kidWM) - canvasBSizeSum;
767           }
768           bOffset = std::max(bOffset, layoutOverflow);
769         }
770       }
771       availableSize.BSize(kidWM) -= bOffset;
772     }
773 
774     if (MOZ_LIKELY(availableSize.BSize(kidWM) > 0)) {
775       ReflowInput kidReflowInput(aPresContext, aReflowInput, kidFrame,
776                                  availableSize);
777 
778       if (aReflowInput.IsBResizeForWM(kidReflowInput.GetWritingMode()) &&
779           kidFrame->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
780         // Tell our kid it's being block-dir resized too.  Bit of a
781         // hack for framesets.
782         kidReflowInput.SetBResize(true);
783       }
784 
785       nsSize containerSize = aReflowInput.ComputedPhysicalSize();
786       LogicalMargin margin = kidReflowInput.ComputedLogicalMargin(kidWM);
787       LogicalPoint kidPt(kidWM, margin.IStart(kidWM), margin.BStart(kidWM));
788       (kidWM.IsOrthogonalTo(wm) ? kidPt.I(kidWM) : kidPt.B(kidWM)) += bOffset;
789 
790       nsReflowStatus kidStatus;
791       ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowInput, kidWM,
792                   kidPt, containerSize, ReflowChildFlags::Default, kidStatus);
793 
794       FinishReflowChild(kidFrame, aPresContext, kidDesiredSize, &kidReflowInput,
795                         kidWM, kidPt, containerSize,
796                         ReflowChildFlags::ApplyRelativePositioning);
797 
798       if (!kidStatus.IsFullyComplete()) {
799         nsIFrame* nextFrame = kidFrame->GetNextInFlow();
800         NS_ASSERTION(nextFrame || kidStatus.NextInFlowNeedsReflow(),
801                      "If it's incomplete and has no nif yet, it must flag a "
802                      "nif reflow.");
803         if (!nextFrame) {
804           nextFrame = aPresContext->PresShell()
805                           ->FrameConstructor()
806                           ->CreateContinuingFrame(kidFrame, this);
807           SetOverflowFrames(nsFrameList(nextFrame, nextFrame));
808           // Root overflow containers will be normal children of
809           // the canvas frame, but that's ok because there
810           // aren't any other frames we need to isolate them from
811           // during reflow.
812         }
813         if (kidStatus.IsOverflowIncomplete()) {
814           nextFrame->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
815         }
816       }
817       aStatus.MergeCompletionStatusFrom(kidStatus);
818 
819       // If the child frame was just inserted, then we're responsible for making
820       // sure it repaints
821       if (kidDirty) {
822         // But we have a new child, which will affect our background, so
823         // invalidate our whole rect.
824         // Note: Even though we request to be sized to our child's size, our
825         // scroll frame ensures that we are always the size of the viewport.
826         // Also note: GetPosition() on a CanvasFrame is always going to return
827         // (0, 0). We only want to invalidate GetRect() since Get*OverflowRect()
828         // could also include overflow to our top and left (out of the viewport)
829         // which doesn't need to be painted.
830         nsIFrame* viewport = PresContext()->GetPresShell()->GetRootFrame();
831         viewport->InvalidateFrame();
832       }
833 
834       // Return our desired size. Normally it's what we're told, but
835       // sometimes we can be given an unconstrained block-size (when a window
836       // is sizing-to-content), and we should compute our desired block-size.
837       if (aReflowInput.ComputedBSize() == NS_UNCONSTRAINEDSIZE &&
838           !kidFrame->IsPlaceholderFrame()) {
839         LogicalSize finalSize = aReflowInput.ComputedSize();
840         finalSize.BSize(wm) =
841             kidFrame->GetLogicalSize(wm).BSize(wm) +
842             kidReflowInput.ComputedLogicalMargin(wm).BStartEnd(wm);
843         aDesiredSize.SetSize(wm, finalSize);
844         aDesiredSize.SetOverflowAreasToDesiredBounds();
845       }
846       aDesiredSize.mOverflowAreas.UnionWith(kidDesiredSize.mOverflowAreas +
847                                             kidFrame->GetPosition());
848     } else if (kidFrame->IsPlaceholderFrame()) {
849       // Placeholders always fit even if there's no available block-size left.
850     } else {
851       // This only occurs in paginated mode.  There is no available space on
852       // this page due to reserving space for overflow from a previous page,
853       // so we push our child to the next page.  Note that we can have some
854       // placeholders for fixed pos. frames in mFrames too, so we need to be
855       // careful to only push `kidFrame`.
856       mFrames.RemoveFrame(kidFrame);
857       SetOverflowFrames(nsFrameList(kidFrame, kidFrame));
858       aStatus.SetIncomplete();
859     }
860   }
861 
862   if (prevCanvasFrame) {
863     ReflowOverflowContainerChildren(aPresContext, aReflowInput,
864                                     aDesiredSize.mOverflowAreas,
865                                     ReflowChildFlags::Default, aStatus);
866   }
867 
868   if (mPopupSetFrame) {
869     MOZ_ASSERT(mFrames.ContainsFrame(mPopupSetFrame),
870                "Only normal flow supported.");
871     nsReflowStatus popupStatus;
872     ReflowOutput popupDesiredSize(aReflowInput.GetWritingMode());
873     WritingMode wm = mPopupSetFrame->GetWritingMode();
874     LogicalSize availSize = aReflowInput.ComputedSize(wm);
875     availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
876     ReflowInput popupReflowInput(aPresContext, aReflowInput, mPopupSetFrame,
877                                  availSize);
878     ReflowChild(mPopupSetFrame, aPresContext, popupDesiredSize,
879                 popupReflowInput, 0, 0, ReflowChildFlags::NoMoveFrame,
880                 popupStatus);
881     FinishReflowChild(mPopupSetFrame, aPresContext, popupDesiredSize,
882                       &popupReflowInput, 0, 0, ReflowChildFlags::NoMoveFrame);
883   }
884 
885   FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput,
886                                  aStatus);
887 
888   NS_FRAME_TRACE_REFLOW_OUT("nsCanvasFrame::Reflow", aStatus);
889   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
890 }
891 
GetContentForEvent(WidgetEvent * aEvent,nsIContent ** aContent)892 nsresult nsCanvasFrame::GetContentForEvent(WidgetEvent* aEvent,
893                                            nsIContent** aContent) {
894   NS_ENSURE_ARG_POINTER(aContent);
895   nsresult rv = nsIFrame::GetContentForEvent(aEvent, aContent);
896   if (NS_FAILED(rv) || !*aContent) {
897     nsIFrame* kid = mFrames.FirstChild();
898     if (kid) {
899       rv = kid->GetContentForEvent(aEvent, aContent);
900     }
901   }
902 
903   return rv;
904 }
905 
906 #ifdef DEBUG_FRAME_DUMP
GetFrameName(nsAString & aResult) const907 nsresult nsCanvasFrame::GetFrameName(nsAString& aResult) const {
908   return MakeFrameName(u"Canvas"_ns, aResult);
909 }
910 #endif
911