Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : /* rendering object that goes directly inside the document's scrollbars */
7 :
8 : #include "nsCanvasFrame.h"
9 :
10 : #include "AccessibleCaretEventHub.h"
11 : #include "gfxContext.h"
12 : #include "gfxUtils.h"
13 : #include "nsContainerFrame.h"
14 : #include "nsCSSRendering.h"
15 : #include "nsPresContext.h"
16 : #include "nsStyleContext.h"
17 : #include "nsGkAtoms.h"
18 : #include "nsIFrameInlines.h"
19 : #include "nsIPresShell.h"
20 : #include "nsDisplayList.h"
21 : #include "nsCSSFrameConstructor.h"
22 : #include "nsFrameManager.h"
23 : #include "gfxPlatform.h"
24 : #include "nsPrintfCString.h"
25 : #include "mozilla/dom/AnonymousContent.h"
26 : #include "mozilla/layers/StackingContextHelper.h"
27 : #include "mozilla/PresShell.h"
28 : // for focus
29 : #include "nsIScrollableFrame.h"
30 : #ifdef DEBUG_CANVAS_FOCUS
31 : #include "nsIDocShell.h"
32 : #endif
33 :
34 : //#define DEBUG_CANVAS_FOCUS
35 :
36 : using namespace mozilla;
37 : using namespace mozilla::dom;
38 : using namespace mozilla::layout;
39 : using namespace mozilla::gfx;
40 : using namespace mozilla::layers;
41 :
42 : nsCanvasFrame*
43 23 : NS_NewCanvasFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
44 : {
45 23 : return new (aPresShell) nsCanvasFrame(aContext);
46 : }
47 :
48 23 : NS_IMPL_FRAMEARENA_HELPERS(nsCanvasFrame)
49 :
50 230 : NS_QUERYFRAME_HEAD(nsCanvasFrame)
51 40 : NS_QUERYFRAME_ENTRY(nsCanvasFrame)
52 23 : NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
53 167 : NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
54 :
55 : void
56 0 : nsCanvasFrame::ShowCustomContentContainer()
57 : {
58 0 : if (mCustomContentContainer) {
59 0 : mCustomContentContainer->UnsetAttr(kNameSpaceID_None, nsGkAtoms::hidden, true);
60 : }
61 0 : }
62 :
63 : void
64 23 : nsCanvasFrame::HideCustomContentContainer()
65 : {
66 23 : if (mCustomContentContainer) {
67 46 : mCustomContentContainer->SetAttr(kNameSpaceID_None, nsGkAtoms::hidden,
68 46 : NS_LITERAL_STRING("true"),
69 69 : true);
70 : }
71 23 : }
72 :
73 : nsresult
74 23 : nsCanvasFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
75 : {
76 23 : if (!mContent) {
77 0 : return NS_OK;
78 : }
79 :
80 46 : nsCOMPtr<nsIDocument> doc = mContent->OwnerDoc();
81 23 : nsresult rv = NS_OK;
82 :
83 : // Create the custom content container.
84 23 : mCustomContentContainer = doc->CreateHTMLElement(nsGkAtoms::div);
85 : #ifdef DEBUG
86 : // We restyle our mCustomContentContainer, even though it's root anonymous
87 : // content. Normally that's not OK because the frame constructor doesn't know
88 : // how to order the frame tree in such cases, but we make this work for this
89 : // particular case, so it's OK.
90 23 : mCustomContentContainer->SetProperty(nsGkAtoms::restylableAnonymousNode,
91 46 : reinterpret_cast<void*>(true));
92 : #endif // DEBUG
93 :
94 23 : aElements.AppendElement(mCustomContentContainer);
95 :
96 : // XXX add :moz-native-anonymous or will that be automatically set?
97 23 : rv = mCustomContentContainer->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
98 46 : NS_LITERAL_STRING("moz-custom-content-container"),
99 46 : true);
100 23 : NS_ENSURE_SUCCESS(rv, rv);
101 :
102 : // Append all existing AnonymousContent nodes stored at document level if any.
103 23 : size_t len = doc->GetAnonymousContents().Length();
104 23 : for (size_t i = 0; i < len; ++i) {
105 0 : nsCOMPtr<Element> node = doc->GetAnonymousContents()[i]->GetContentNode();
106 0 : mCustomContentContainer->AppendChildTo(node->AsContent(), true);
107 : }
108 :
109 : // Only create a frame for mCustomContentContainer if it has some children.
110 23 : if (len == 0) {
111 23 : HideCustomContentContainer();
112 : }
113 :
114 : RefPtr<AccessibleCaretEventHub> eventHub =
115 46 : PresContext()->GetPresShell()->GetAccessibleCaretEventHub();
116 23 : if (eventHub) {
117 : // AccessibleCaret will insert anonymous caret elements.
118 0 : eventHub->Init();
119 : }
120 :
121 23 : return NS_OK;
122 : }
123 :
124 : void
125 0 : nsCanvasFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements, uint32_t aFilter)
126 : {
127 0 : if (mCustomContentContainer) {
128 0 : aElements.AppendElement(mCustomContentContainer);
129 : }
130 0 : }
131 :
132 : void
133 0 : nsCanvasFrame::DestroyFrom(nsIFrame* aDestructRoot)
134 : {
135 : nsIScrollableFrame* sf =
136 0 : PresContext()->GetPresShell()->GetRootScrollFrameAsScrollable();
137 0 : if (sf) {
138 0 : sf->RemoveScrollPositionListener(this);
139 : }
140 :
141 : // Elements inserted in the custom content container have the same lifetime as
142 : // the document, so before destroying the container, make sure to keep a clone
143 : // of each of them at document level so they can be re-appended on reframe.
144 0 : if (mCustomContentContainer) {
145 0 : nsCOMPtr<nsIDocument> doc = mContent->OwnerDoc();
146 0 : ErrorResult rv;
147 :
148 : nsTArray<RefPtr<mozilla::dom::AnonymousContent>>& docAnonContents =
149 0 : doc->GetAnonymousContents();
150 0 : for (size_t i = 0, len = docAnonContents.Length(); i < len; ++i) {
151 0 : AnonymousContent* content = docAnonContents[i];
152 0 : nsCOMPtr<nsINode> clonedElement = content->GetContentNode()->CloneNode(true, rv);
153 0 : content->SetContentNode(clonedElement->AsElement());
154 : }
155 : }
156 0 : nsContentUtils::DestroyAnonymousContent(&mCustomContentContainer);
157 :
158 0 : nsContainerFrame::DestroyFrom(aDestructRoot);
159 0 : }
160 :
161 : void
162 0 : nsCanvasFrame::ScrollPositionWillChange(nscoord aX, nscoord aY)
163 : {
164 0 : if (mDoPaintFocus) {
165 0 : mDoPaintFocus = false;
166 0 : PresContext()->FrameManager()->GetRootFrame()->InvalidateFrameSubtree();
167 : }
168 0 : }
169 :
170 : NS_IMETHODIMP
171 0 : nsCanvasFrame::SetHasFocus(bool aHasFocus)
172 : {
173 0 : if (mDoPaintFocus != aHasFocus) {
174 0 : mDoPaintFocus = aHasFocus;
175 0 : PresContext()->FrameManager()->GetRootFrame()->InvalidateFrameSubtree();
176 :
177 0 : if (!mAddedScrollPositionListener) {
178 : nsIScrollableFrame* sf =
179 0 : PresContext()->GetPresShell()->GetRootScrollFrameAsScrollable();
180 0 : if (sf) {
181 0 : sf->AddScrollPositionListener(this);
182 0 : mAddedScrollPositionListener = true;
183 : }
184 : }
185 : }
186 0 : return NS_OK;
187 : }
188 :
189 : void
190 23 : nsCanvasFrame::SetInitialChildList(ChildListID aListID,
191 : nsFrameList& aChildList)
192 : {
193 23 : NS_ASSERTION(aListID != kPrincipalList ||
194 : aChildList.IsEmpty() || aChildList.OnlyChild(),
195 : "Primary child list can have at most one frame in it");
196 23 : nsContainerFrame::SetInitialChildList(aListID, aChildList);
197 23 : MaybePropagateRootElementWritingMode();
198 23 : }
199 :
200 : void
201 23 : nsCanvasFrame::AppendFrames(ChildListID aListID,
202 : nsFrameList& aFrameList)
203 : {
204 : #ifdef DEBUG
205 23 : MOZ_ASSERT(aListID == kPrincipalList, "unexpected child list");
206 23 : if (!mFrames.IsEmpty()) {
207 23 : for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
208 : // We only allow native anonymous child frames to be in principal child
209 : // list in canvas frame.
210 0 : MOZ_ASSERT(e.get()->GetContent()->IsInNativeAnonymousSubtree(),
211 : "invalid child list");
212 : }
213 : }
214 23 : nsFrame::VerifyDirtyBitSet(aFrameList);
215 : #endif
216 23 : nsContainerFrame::AppendFrames(aListID, aFrameList);
217 23 : MaybePropagateRootElementWritingMode();
218 23 : }
219 :
220 : void
221 0 : nsCanvasFrame::InsertFrames(ChildListID aListID,
222 : nsIFrame* aPrevFrame,
223 : nsFrameList& aFrameList)
224 : {
225 : // Because we only support a single child frame inserting is the same
226 : // as appending
227 0 : MOZ_ASSERT(!aPrevFrame, "unexpected previous sibling frame");
228 0 : AppendFrames(aListID, aFrameList);
229 0 : MaybePropagateRootElementWritingMode();
230 0 : }
231 :
232 : #ifdef DEBUG
233 : void
234 0 : nsCanvasFrame::RemoveFrame(ChildListID aListID,
235 : nsIFrame* aOldFrame)
236 : {
237 0 : MOZ_ASSERT(aListID == kPrincipalList, "unexpected child list");
238 0 : nsContainerFrame::RemoveFrame(aListID, aOldFrame);
239 0 : }
240 : #endif
241 :
242 69 : nsRect nsCanvasFrame::CanvasArea() const
243 : {
244 : // Not clear which overflow rect we want here, but it probably doesn't
245 : // matter.
246 69 : nsRect result(GetVisualOverflowRect());
247 :
248 69 : nsIScrollableFrame *scrollableFrame = do_QueryFrame(GetParent());
249 69 : if (scrollableFrame) {
250 138 : nsRect portRect = scrollableFrame->GetScrollPortRect();
251 69 : result.UnionRect(result, nsRect(nsPoint(0, 0), portRect.Size()));
252 : }
253 69 : return result;
254 : }
255 :
256 : void
257 1 : nsDisplayCanvasBackgroundColor::Paint(nsDisplayListBuilder* aBuilder,
258 : gfxContext* aCtx)
259 : {
260 1 : nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
261 1 : nsPoint offset = ToReferenceFrame();
262 2 : nsRect bgClipRect = frame->CanvasArea() + offset;
263 1 : if (NS_GET_A(mColor) > 0) {
264 1 : DrawTarget* drawTarget = aCtx->GetDrawTarget();
265 1 : int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
266 : Rect devPxRect =
267 1 : NSRectToSnappedRect(bgClipRect, appUnitsPerDevPixel, *drawTarget);
268 1 : drawTarget->FillRect(devPxRect, ColorPattern(ToDeviceColor(mColor)));
269 : }
270 1 : }
271 :
272 : already_AddRefed<Layer>
273 0 : nsDisplayCanvasBackgroundColor::BuildLayer(nsDisplayListBuilder* aBuilder,
274 : LayerManager* aManager,
275 : const ContainerLayerParameters& aContainerParameters)
276 : {
277 0 : if (NS_GET_A(mColor) == 0) {
278 0 : return nullptr;
279 : }
280 :
281 0 : if (aManager->GetBackendType() == layers::LayersBackend::LAYERS_WR) {
282 0 : return BuildDisplayItemLayer(aBuilder, aManager, aContainerParameters);
283 : }
284 :
285 : RefPtr<ColorLayer> layer = static_cast<ColorLayer*>
286 0 : (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
287 0 : if (!layer) {
288 0 : layer = aManager->CreateColorLayer();
289 0 : if (!layer) {
290 0 : return nullptr;
291 : }
292 : }
293 0 : layer->SetColor(ToDeviceColor(mColor));
294 :
295 0 : nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
296 0 : nsPoint offset = ToReferenceFrame();
297 0 : nsRect bgClipRect = frame->CanvasArea() + offset;
298 :
299 0 : int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
300 :
301 0 : layer->SetBounds(bgClipRect.ToNearestPixels(appUnitsPerDevPixel));
302 0 : layer->SetBaseTransform(gfx::Matrix4x4::Translation(aContainerParameters.mOffset.x,
303 0 : aContainerParameters.mOffset.y, 0));
304 :
305 0 : return layer.forget();
306 : }
307 :
308 : bool
309 0 : nsDisplayCanvasBackgroundColor::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
310 : const StackingContextHelper& aSc,
311 : nsTArray<WebRenderParentCommand>& aParentCommands,
312 : WebRenderLayerManager* aManager,
313 : nsDisplayListBuilder* aDisplayListBuilder)
314 : {
315 0 : if (aManager->IsLayersFreeTransaction()) {
316 0 : ContainerLayerParameters parameter;
317 0 : if (GetLayerState(aDisplayListBuilder, aManager, parameter) != LAYER_ACTIVE) {
318 0 : return false;
319 : }
320 : }
321 :
322 0 : nsCanvasFrame *frame = static_cast<nsCanvasFrame *>(mFrame);
323 0 : nsPoint offset = ToReferenceFrame();
324 0 : nsRect bgClipRect = frame->CanvasArea() + offset;
325 0 : int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
326 :
327 : LayoutDeviceRect rect = LayoutDeviceRect::FromAppUnits(
328 0 : bgClipRect, appUnitsPerDevPixel);
329 :
330 0 : WrRect transformedRect = aSc.ToRelativeWrRect(rect);
331 : aBuilder.PushRect(transformedRect,
332 : transformedRect,
333 0 : wr::ToWrColor(ToDeviceColor(mColor)));
334 0 : return true;
335 : }
336 :
337 : #ifdef MOZ_DUMP_PAINTING
338 : void
339 0 : nsDisplayCanvasBackgroundColor::WriteDebugInfo(std::stringstream& aStream)
340 : {
341 0 : aStream << " (rgba "
342 0 : << (int)NS_GET_R(mColor) << ","
343 0 : << (int)NS_GET_G(mColor) << ","
344 0 : << (int)NS_GET_B(mColor) << ","
345 0 : << (int)NS_GET_A(mColor) << ")";
346 0 : }
347 : #endif
348 :
349 : #ifndef MOZ_GFX_OPTIMIZE_MOBILE
350 0 : static void BlitSurface(DrawTarget* aDest, const gfxRect& aRect, DrawTarget* aSource)
351 : {
352 0 : RefPtr<SourceSurface> source = aSource->Snapshot();
353 0 : aDest->DrawSurface(source,
354 0 : Rect(aRect.x, aRect.y, aRect.width, aRect.height),
355 0 : Rect(0, 0, aRect.width, aRect.height));
356 0 : }
357 : #endif
358 :
359 : void
360 0 : nsDisplayCanvasBackgroundImage::Paint(nsDisplayListBuilder* aBuilder,
361 : gfxContext* aCtx)
362 : {
363 0 : nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
364 0 : nsPoint offset = ToReferenceFrame();
365 0 : nsRect bgClipRect = frame->CanvasArea() + offset;
366 :
367 : #ifndef MOZ_GFX_OPTIMIZE_MOBILE
368 0 : RefPtr<gfxContext> dest = aCtx;
369 0 : gfxRect destRect;
370 0 : if (IsSingleFixedPositionImage(aBuilder, bgClipRect, &destRect) &&
371 0 : aBuilder->IsPaintingToWindow() && !aBuilder->IsCompositingCheap() &&
372 0 : !dest->CurrentMatrix().HasNonIntegerTranslation()) {
373 : // Snap image rectangle to nearest pixel boundaries. This is the right way
374 : // to snap for this context, because we checked HasNonIntegerTranslation
375 : // above.
376 0 : destRect.Round();
377 : RefPtr<DrawTarget> dt =
378 0 : Frame()->GetProperty(nsIFrame::CachedBackgroundImageDT());
379 0 : DrawTarget* destDT = dest->GetDrawTarget();
380 0 : if (dt) {
381 0 : BlitSurface(destDT, destRect, dt);
382 0 : return;
383 : }
384 :
385 0 : dt = destDT->CreateSimilarDrawTarget(IntSize::Ceil(destRect.width,
386 0 : destRect.height),
387 0 : SurfaceFormat::B8G8R8A8);
388 0 : if (dt && dt->IsValid()) {
389 0 : RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
390 0 : MOZ_ASSERT(ctx); // already checked draw target above
391 0 : ctx->SetMatrix(ctx->CurrentMatrix().PreTranslate(-destRect.x, -destRect.y));
392 0 : PaintInternal(aBuilder, ctx, bgClipRect, &bgClipRect);
393 0 : BlitSurface(dest->GetDrawTarget(), destRect, dt);
394 0 : frame->SetProperty(nsIFrame::CachedBackgroundImageDT(),
395 0 : dt.forget().take());
396 0 : return;
397 : }
398 : }
399 : #endif
400 0 : PaintInternal(aBuilder, aCtx, mVisibleRect, &bgClipRect);
401 : }
402 :
403 : bool
404 0 : nsDisplayCanvasBackgroundImage::IsSingleFixedPositionImage(nsDisplayListBuilder* aBuilder,
405 : const nsRect& aClipRect,
406 : gfxRect* aDestRect)
407 : {
408 0 : if (!mBackgroundStyle)
409 0 : return false;
410 :
411 0 : if (mBackgroundStyle->mImage.mLayers.Length() != 1)
412 0 : return false;
413 :
414 :
415 0 : nsPresContext* presContext = mFrame->PresContext();
416 0 : uint32_t flags = aBuilder->GetBackgroundPaintFlags();
417 0 : nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
418 0 : const nsStyleImageLayers::Layer &layer = mBackgroundStyle->mImage.mLayers[mLayer];
419 :
420 0 : if (layer.mAttachment != NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED)
421 0 : return false;
422 :
423 : nsBackgroundLayerState state =
424 : nsCSSRendering::PrepareImageLayer(presContext, mFrame, flags,
425 0 : borderArea, aClipRect, layer);
426 :
427 :
428 : // We only care about images here, not gradients.
429 0 : if (!mIsRasterImage)
430 0 : return false;
431 :
432 0 : int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
433 0 : *aDestRect = nsLayoutUtils::RectToGfxRect(state.mFillArea, appUnitsPerDevPixel);
434 :
435 0 : return true;
436 : }
437 :
438 :
439 : void
440 0 : nsDisplayCanvasThemedBackground::Paint(nsDisplayListBuilder* aBuilder,
441 : gfxContext* aCtx)
442 : {
443 0 : nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
444 0 : nsPoint offset = ToReferenceFrame();
445 0 : nsRect bgClipRect = frame->CanvasArea() + offset;
446 :
447 0 : PaintInternal(aBuilder, aCtx, mVisibleRect, &bgClipRect);
448 0 : }
449 :
450 : /**
451 : * A display item to paint the focus ring for the document.
452 : *
453 : * The only reason this can't use nsDisplayGeneric is overriding GetBounds.
454 : */
455 : class nsDisplayCanvasFocus : public nsDisplayItem {
456 : public:
457 0 : nsDisplayCanvasFocus(nsDisplayListBuilder* aBuilder, nsCanvasFrame *aFrame)
458 0 : : nsDisplayItem(aBuilder, aFrame)
459 : {
460 0 : MOZ_COUNT_CTOR(nsDisplayCanvasFocus);
461 0 : }
462 0 : virtual ~nsDisplayCanvasFocus() {
463 0 : MOZ_COUNT_DTOR(nsDisplayCanvasFocus);
464 0 : }
465 :
466 0 : virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
467 : bool* aSnap) override
468 : {
469 0 : *aSnap = false;
470 : // This is an overestimate, but that's not a problem.
471 0 : nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
472 0 : return frame->CanvasArea() + ToReferenceFrame();
473 : }
474 :
475 0 : virtual void Paint(nsDisplayListBuilder* aBuilder,
476 : gfxContext* aCtx) override
477 : {
478 0 : nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
479 0 : frame->PaintFocus(aCtx->GetDrawTarget(), ToReferenceFrame());
480 0 : }
481 :
482 0 : NS_DISPLAY_DECL_NAME("CanvasFocus", TYPE_CANVAS_FOCUS)
483 : };
484 :
485 : void
486 20 : nsCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
487 : const nsRect& aDirtyRect,
488 : const nsDisplayListSet& aLists)
489 : {
490 20 : if (GetPrevInFlow()) {
491 0 : DisplayOverflowContainers(aBuilder, aDirtyRect, aLists);
492 : }
493 :
494 : // Force a background to be shown. We may have a background propagated to us,
495 : // in which case StyleBackground wouldn't have the right background
496 : // and the code in nsFrame::DisplayBorderBackgroundOutline might not give us
497 : // a background.
498 : // We don't have any border or outline, and our background draws over
499 : // the overflow area, so just add nsDisplayCanvasBackground instead of
500 : // calling DisplayBorderBackgroundOutline.
501 20 : if (IsVisibleForPainting(aBuilder)) {
502 : nsStyleContext* bgSC;
503 20 : const nsStyleBackground* bg = nullptr;
504 20 : bool isThemed = IsThemed();
505 20 : if (!isThemed && nsCSSRendering::FindBackground(this, &bgSC)) {
506 20 : bg = bgSC->StyleBackground();
507 : }
508 20 : aLists.BorderBackground()->AppendNewToTop(
509 40 : new (aBuilder) nsDisplayCanvasBackgroundColor(aBuilder, this));
510 :
511 20 : if (isThemed) {
512 0 : aLists.BorderBackground()->AppendNewToTop(
513 0 : new (aBuilder) nsDisplayCanvasThemedBackground(aBuilder, this));
514 0 : return;
515 : }
516 :
517 20 : if (!bg) {
518 0 : return;
519 : }
520 :
521 : const ActiveScrolledRoot* asr =
522 20 : aBuilder->CurrentActiveScrolledRoot();
523 :
524 20 : bool needBlendContainer = false;
525 40 : nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
526 :
527 : // Create separate items for each background layer.
528 20 : const nsStyleImageLayers& layers = bg->mImage;
529 40 : NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, layers) {
530 20 : if (layers.mLayers[i].mImage.IsEmpty()) {
531 20 : continue;
532 : }
533 0 : if (layers.mLayers[i].mBlendMode != NS_STYLE_BLEND_NORMAL) {
534 0 : needBlendContainer = true;
535 : }
536 :
537 0 : nsRect bgRect = GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this);
538 :
539 0 : const ActiveScrolledRoot* thisItemASR = asr;
540 0 : nsDisplayList thisItemList;
541 : nsDisplayBackgroundImage::InitData bgData =
542 : nsDisplayBackgroundImage::GetInitData(aBuilder, this, i, bgRect, bg,
543 0 : nsDisplayBackgroundImage::LayerizeFixed::ALWAYS_LAYERIZE_FIXED_BACKGROUND);
544 :
545 0 : if (bgData.shouldFixToViewport) {
546 :
547 0 : auto* displayData = aBuilder->GetCurrentFixedBackgroundDisplayData();
548 : nsDisplayListBuilder::AutoBuildingDisplayList
549 0 : buildingDisplayList(aBuilder, this, aBuilder->GetDirtyRect(), false);
550 :
551 0 : DisplayListClipState::AutoSaveRestore clipState(aBuilder);
552 0 : nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
553 0 : if (displayData) {
554 0 : nsRect dirtyRect = displayData->mDirtyRect + GetOffsetTo(PresContext()->GetPresShell()->GetRootFrame());
555 0 : buildingDisplayList.SetDirtyRect(dirtyRect);
556 0 : clipState.SetClipChainForContainingBlockDescendants(
557 0 : displayData->mContainingBlockClipChain);
558 0 : asrSetter.SetCurrentActiveScrolledRoot(
559 0 : displayData->mContainingBlockActiveScrolledRoot);
560 0 : thisItemASR = displayData->mContainingBlockActiveScrolledRoot;
561 : }
562 0 : nsDisplayCanvasBackgroundImage* bgItem = nullptr;
563 : {
564 0 : DisplayListClipState::AutoSaveRestore bgImageClip(aBuilder);
565 0 : bgImageClip.Clear();
566 0 : bgItem = new (aBuilder) nsDisplayCanvasBackgroundImage(bgData);
567 : }
568 : thisItemList.AppendNewToTop(
569 0 : nsDisplayFixedPosition::CreateForFixedBackground(aBuilder, this, bgItem, i));
570 :
571 : } else {
572 0 : thisItemList.AppendNewToTop(new (aBuilder) nsDisplayCanvasBackgroundImage(bgData));
573 : }
574 :
575 0 : if (layers.mLayers[i].mBlendMode != NS_STYLE_BLEND_NORMAL) {
576 0 : DisplayListClipState::AutoSaveRestore blendClip(aBuilder);
577 0 : blendClip.ClearUpToASR(thisItemASR);
578 : thisItemList.AppendNewToTop(
579 : new (aBuilder) nsDisplayBlendMode(aBuilder, this, &thisItemList,
580 0 : layers.mLayers[i].mBlendMode,
581 0 : thisItemASR, i + 1));
582 : }
583 0 : aLists.BorderBackground()->AppendToTop(&thisItemList);
584 : }
585 :
586 20 : if (needBlendContainer) {
587 0 : const ActiveScrolledRoot* containerASR = contASRTracker.GetContainerASR();
588 0 : DisplayListClipState::AutoSaveRestore blendContainerClip(aBuilder);
589 0 : blendContainerClip.ClearUpToASR(containerASR);
590 0 : aLists.BorderBackground()->AppendNewToTop(
591 0 : nsDisplayBlendContainer::CreateForBackgroundBlendMode(aBuilder, this,
592 : aLists.BorderBackground(),
593 0 : containerASR));
594 : }
595 : }
596 :
597 40 : for (nsIFrame* kid : PrincipalChildList()) {
598 : // Put our child into its own pseudo-stack.
599 20 : BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
600 : }
601 :
602 : #ifdef DEBUG_CANVAS_FOCUS
603 : nsCOMPtr<nsIContent> focusContent;
604 : aPresContext->EventStateManager()->
605 : GetFocusedContent(getter_AddRefs(focusContent));
606 :
607 : bool hasFocus = false;
608 : nsCOMPtr<nsISupports> container;
609 : aPresContext->GetContainer(getter_AddRefs(container));
610 : nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
611 : if (docShell) {
612 : docShell->GetHasFocus(&hasFocus);
613 : printf("%p - nsCanvasFrame::Paint R:%d,%d,%d,%d DR: %d,%d,%d,%d\n", this,
614 : mRect.x, mRect.y, mRect.width, mRect.height,
615 : aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height);
616 : }
617 : printf("%p - Focus: %s c: %p DoPaint:%s\n", docShell.get(), hasFocus?"Y":"N",
618 : focusContent.get(), mDoPaintFocus?"Y":"N");
619 : #endif
620 :
621 20 : if (!mDoPaintFocus)
622 20 : return;
623 : // Only paint the focus if we're visible
624 0 : if (!StyleVisibility()->IsVisible())
625 0 : return;
626 :
627 0 : aLists.Outlines()->AppendNewToTop(new (aBuilder)
628 0 : nsDisplayCanvasFocus(aBuilder, this));
629 : }
630 :
631 : void
632 0 : nsCanvasFrame::PaintFocus(DrawTarget* aDrawTarget, nsPoint aPt)
633 : {
634 0 : nsRect focusRect(aPt, GetSize());
635 :
636 0 : nsIScrollableFrame *scrollableFrame = do_QueryFrame(GetParent());
637 0 : if (scrollableFrame) {
638 0 : nsRect portRect = scrollableFrame->GetScrollPortRect();
639 0 : focusRect.width = portRect.width;
640 0 : focusRect.height = portRect.height;
641 0 : focusRect.MoveBy(scrollableFrame->GetScrollPosition());
642 : }
643 :
644 : // XXX use the root frame foreground color, but should we find BODY frame
645 : // for HTML documents?
646 0 : nsIFrame* root = mFrames.FirstChild();
647 0 : const nsStyleColor* color = root ? root->StyleColor() : StyleColor();
648 0 : if (!color) {
649 0 : NS_ERROR("current color cannot be found");
650 0 : return;
651 : }
652 :
653 0 : nsCSSRendering::PaintFocus(PresContext(), aDrawTarget,
654 0 : focusRect, color->mColor);
655 : }
656 :
657 : /* virtual */ nscoord
658 0 : nsCanvasFrame::GetMinISize(gfxContext *aRenderingContext)
659 : {
660 : nscoord result;
661 0 : DISPLAY_MIN_WIDTH(this, result);
662 0 : if (mFrames.IsEmpty())
663 0 : result = 0;
664 : else
665 0 : result = mFrames.FirstChild()->GetMinISize(aRenderingContext);
666 0 : return result;
667 : }
668 :
669 : /* virtual */ nscoord
670 0 : nsCanvasFrame::GetPrefISize(gfxContext *aRenderingContext)
671 : {
672 : nscoord result;
673 0 : DISPLAY_PREF_WIDTH(this, result);
674 0 : if (mFrames.IsEmpty())
675 0 : result = 0;
676 : else
677 0 : result = mFrames.FirstChild()->GetPrefISize(aRenderingContext);
678 0 : return result;
679 : }
680 :
681 : void
682 45 : nsCanvasFrame::Reflow(nsPresContext* aPresContext,
683 : ReflowOutput& aDesiredSize,
684 : const ReflowInput& aReflowInput,
685 : nsReflowStatus& aStatus)
686 : {
687 45 : MarkInReflow();
688 45 : DO_GLOBAL_REFLOW_COUNT("nsCanvasFrame");
689 90 : DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
690 45 : NS_FRAME_TRACE_REFLOW_IN("nsCanvasFrame::Reflow");
691 :
692 : // Initialize OUT parameter
693 45 : aStatus.Reset();
694 :
695 : nsCanvasFrame* prevCanvasFrame = static_cast<nsCanvasFrame*>
696 45 : (GetPrevInFlow());
697 45 : if (prevCanvasFrame) {
698 : AutoFrameListPtr overflow(aPresContext,
699 0 : prevCanvasFrame->StealOverflowFrames());
700 0 : if (overflow) {
701 0 : NS_ASSERTION(overflow->OnlyChild(),
702 : "must have doc root as canvas frame's only child");
703 0 : 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 0 : 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 45 : 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 90 : ReflowOutput kidDesiredSize(aReflowInput);
724 45 : if (mFrames.IsEmpty()) {
725 : // We have no child frame, so return an empty size
726 0 : aDesiredSize.Width() = aDesiredSize.Height() = 0;
727 : } else {
728 45 : nsIFrame* kidFrame = mFrames.FirstChild();
729 45 : bool kidDirty = (kidFrame->GetStateBits() & NS_FRAME_IS_DIRTY) != 0;
730 :
731 : ReflowInput
732 : kidReflowInput(aPresContext, aReflowInput, kidFrame,
733 45 : aReflowInput.AvailableSize(kidFrame->GetWritingMode()));
734 :
735 67 : if (aReflowInput.IsBResizeForWM(kidReflowInput.GetWritingMode()) &&
736 22 : (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 0 : kidReflowInput.SetBResize(true);
740 : }
741 :
742 45 : WritingMode wm = aReflowInput.GetWritingMode();
743 45 : WritingMode kidWM = kidReflowInput.GetWritingMode();
744 45 : nsSize containerSize = aReflowInput.ComputedPhysicalSize();
745 :
746 45 : LogicalMargin margin = kidReflowInput.ComputedLogicalMargin();
747 45 : LogicalPoint kidPt(kidWM, margin.IStart(kidWM), margin.BStart(kidWM));
748 :
749 45 : kidReflowInput.ApplyRelativePositioning(&kidPt, containerSize);
750 :
751 : // Reflow the frame
752 45 : ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowInput,
753 45 : kidWM, kidPt, containerSize, 0, aStatus);
754 :
755 : // Complete the reflow and position and size the child frame
756 : FinishReflowChild(kidFrame, aPresContext, kidDesiredSize, &kidReflowInput,
757 45 : kidWM, kidPt, containerSize, 0);
758 :
759 45 : if (!aStatus.IsFullyComplete()) {
760 0 : nsIFrame* nextFrame = kidFrame->GetNextInFlow();
761 0 : NS_ASSERTION(nextFrame || aStatus.NextInFlowNeedsReflow(),
762 : "If it's incomplete and has no nif yet, it must flag a nif reflow.");
763 0 : if (!nextFrame) {
764 : nextFrame = aPresContext->PresShell()->FrameConstructor()->
765 0 : CreateContinuingFrame(aPresContext, kidFrame, this);
766 0 : SetOverflowFrames(nsFrameList(nextFrame, nextFrame));
767 : // Root overflow containers will be normal children of
768 : // the canvas frame, but that's ok because there
769 : // aren't any other frames we need to isolate them from
770 : // during reflow.
771 : }
772 0 : if (aStatus.IsOverflowIncomplete()) {
773 0 : nextFrame->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
774 : }
775 : }
776 :
777 : // If the child frame was just inserted, then we're responsible for making sure
778 : // it repaints
779 45 : if (kidDirty) {
780 : // But we have a new child, which will affect our background, so
781 : // invalidate our whole rect.
782 : // Note: Even though we request to be sized to our child's size, our
783 : // scroll frame ensures that we are always the size of the viewport.
784 : // Also note: GetPosition() on a CanvasFrame is always going to return
785 : // (0, 0). We only want to invalidate GetRect() since Get*OverflowRect()
786 : // could also include overflow to our top and left (out of the viewport)
787 : // which doesn't need to be painted.
788 24 : nsIFrame* viewport = PresContext()->GetPresShell()->GetRootFrame();
789 24 : viewport->InvalidateFrame();
790 : }
791 :
792 : // Return our desired size. Normally it's what we're told, but
793 : // sometimes we can be given an unconstrained height (when a window
794 : // is sizing-to-content), and we should compute our desired height.
795 45 : LogicalSize finalSize(wm);
796 45 : finalSize.ISize(wm) = aReflowInput.ComputedISize();
797 45 : if (aReflowInput.ComputedBSize() == NS_UNCONSTRAINEDSIZE) {
798 0 : finalSize.BSize(wm) = kidFrame->GetLogicalSize(wm).BSize(wm) +
799 0 : kidReflowInput.ComputedLogicalMargin().BStartEnd(wm);
800 : } else {
801 45 : finalSize.BSize(wm) = aReflowInput.ComputedBSize();
802 : }
803 :
804 45 : aDesiredSize.SetSize(wm, finalSize);
805 45 : aDesiredSize.SetOverflowAreasToDesiredBounds();
806 45 : aDesiredSize.mOverflowAreas.UnionWith(
807 90 : kidDesiredSize.mOverflowAreas + kidFrame->GetPosition());
808 : }
809 :
810 45 : if (prevCanvasFrame) {
811 0 : ReflowOverflowContainerChildren(aPresContext, aReflowInput,
812 : aDesiredSize.mOverflowAreas, 0,
813 0 : aStatus);
814 : }
815 :
816 45 : FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus);
817 :
818 45 : NS_FRAME_TRACE_REFLOW_OUT("nsCanvasFrame::Reflow", aStatus);
819 45 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
820 45 : }
821 :
822 : nsresult
823 0 : nsCanvasFrame::GetContentForEvent(WidgetEvent* aEvent, nsIContent** aContent)
824 : {
825 0 : NS_ENSURE_ARG_POINTER(aContent);
826 0 : nsresult rv = nsFrame::GetContentForEvent(aEvent,
827 0 : aContent);
828 0 : if (NS_FAILED(rv) || !*aContent) {
829 0 : nsIFrame* kid = mFrames.FirstChild();
830 0 : if (kid) {
831 : rv = kid->GetContentForEvent(aEvent,
832 0 : aContent);
833 : }
834 : }
835 :
836 0 : return rv;
837 : }
838 :
839 : void
840 46 : nsCanvasFrame::MaybePropagateRootElementWritingMode()
841 : {
842 46 : nsIFrame* child = PrincipalChildList().FirstChild();
843 92 : if (child && child->GetContent() &&
844 46 : child->GetContent() == PresContext()->Document()->GetRootElement()) {
845 46 : nsIFrame* childPrimary = child->GetContent()->GetPrimaryFrame();
846 46 : PropagateRootElementWritingMode(childPrimary->GetWritingMode());
847 : }
848 46 : }
849 :
850 : #ifdef DEBUG_FRAME_DUMP
851 : nsresult
852 0 : nsCanvasFrame::GetFrameName(nsAString& aResult) const
853 : {
854 0 : return MakeFrameName(NS_LITERAL_STRING("Canvas"), aResult);
855 : }
856 : #endif
|