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