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