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/. */
6 /* rendering object for the HTML <canvas> element */
8 #include "nsHTMLCanvasFrame.h"
10 #include "nsGkAtoms.h"
11 #include "mozilla/dom/HTMLCanvasElement.h"
12 #include "nsDisplayList.h"
13 #include "nsLayoutUtils.h"
15 #include "ActiveLayerTracker.h"
19 using namespace mozilla
;
20 using namespace mozilla::dom
;
21 using namespace mozilla::layers
;
23 class nsDisplayCanvas
: public nsDisplayItem
{
25 nsDisplayCanvas(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
)
26 : nsDisplayItem(aBuilder
, aFrame
)
28 MOZ_COUNT_CTOR(nsDisplayCanvas
);
30 #ifdef NS_BUILD_REFCNT_LOGGING
31 virtual ~nsDisplayCanvas() {
32 MOZ_COUNT_DTOR(nsDisplayCanvas
);
36 NS_DISPLAY_DECL_NAME("nsDisplayCanvas", TYPE_CANVAS
)
38 virtual nsRegion
GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
39 bool* aSnap
) MOZ_OVERRIDE
{
41 nsIFrame
* f
= Frame();
42 HTMLCanvasElement
*canvas
=
43 HTMLCanvasElement::FromContent(f
->GetContent());
45 if (canvas
->GetIsOpaque()) {
46 result
= GetBounds(aBuilder
, aSnap
);
51 virtual nsRect
GetBounds(nsDisplayListBuilder
* aBuilder
,
52 bool* aSnap
) MOZ_OVERRIDE
{
54 nsHTMLCanvasFrame
* f
= static_cast<nsHTMLCanvasFrame
*>(Frame());
55 return f
->GetInnerArea() + ToReferenceFrame();
58 virtual already_AddRefed
<Layer
> BuildLayer(nsDisplayListBuilder
* aBuilder
,
59 LayerManager
* aManager
,
60 const ContainerLayerParameters
& aContainerParameters
) MOZ_OVERRIDE
62 return static_cast<nsHTMLCanvasFrame
*>(mFrame
)->
63 BuildLayer(aBuilder
, aManager
, this, aContainerParameters
);
65 virtual LayerState
GetLayerState(nsDisplayListBuilder
* aBuilder
,
66 LayerManager
* aManager
,
67 const ContainerLayerParameters
& aParameters
) MOZ_OVERRIDE
69 if (HTMLCanvasElement::FromContent(mFrame
->GetContent())->ShouldForceInactiveLayer(aManager
))
70 return LAYER_INACTIVE
;
72 // If compositing is cheap, just do that
73 if (aManager
->IsCompositingCheap() ||
74 ActiveLayerTracker::IsContentActive(mFrame
))
75 return mozilla::LAYER_ACTIVE
;
77 return LAYER_INACTIVE
;
83 NS_NewHTMLCanvasFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
85 return new (aPresShell
) nsHTMLCanvasFrame(aContext
);
88 NS_QUERYFRAME_HEAD(nsHTMLCanvasFrame
)
89 NS_QUERYFRAME_ENTRY(nsHTMLCanvasFrame
)
90 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame
)
92 NS_IMPL_FRAMEARENA_HELPERS(nsHTMLCanvasFrame
)
95 nsHTMLCanvasFrame::Init(nsIContent
* aContent
,
96 nsContainerFrame
* aParent
,
97 nsIFrame
* aPrevInFlow
)
99 nsContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
101 // We can fill in the canvas before the canvas frame is created, in
102 // which case we never get around to marking the content as active. Therefore,
103 // we mark it active here when we create the frame.
104 ActiveLayerTracker::NotifyContentChange(this);
107 nsHTMLCanvasFrame::~nsHTMLCanvasFrame()
112 nsHTMLCanvasFrame::GetCanvasSize()
115 HTMLCanvasElement
*canvas
=
116 HTMLCanvasElement::FromContentOrNull(GetContent());
118 size
= canvas
->GetSize();
120 NS_NOTREACHED("couldn't get canvas size");
126 /* virtual */ nscoord
127 nsHTMLCanvasFrame::GetMinISize(nsRenderingContext
*aRenderingContext
)
129 // XXX The caller doesn't account for constraints of the height,
130 // min-height, and max-height properties.
131 nscoord result
= nsPresContext::CSSPixelsToAppUnits(GetCanvasSize().width
);
132 DISPLAY_MIN_WIDTH(this, result
);
136 /* virtual */ nscoord
137 nsHTMLCanvasFrame::GetPrefISize(nsRenderingContext
*aRenderingContext
)
139 // XXX The caller doesn't account for constraints of the height,
140 // min-height, and max-height properties.
141 nscoord result
= nsPresContext::CSSPixelsToAppUnits(GetCanvasSize().width
);
142 DISPLAY_PREF_WIDTH(this, result
);
147 nsHTMLCanvasFrame::GetIntrinsicRatio()
149 nsIntSize
size(GetCanvasSize());
150 return nsSize(nsPresContext::CSSPixelsToAppUnits(size
.width
),
151 nsPresContext::CSSPixelsToAppUnits(size
.height
));
156 nsHTMLCanvasFrame::ComputeSize(nsRenderingContext
*aRenderingContext
,
158 const LogicalSize
& aCBSize
,
159 nscoord aAvailableISize
,
160 const LogicalSize
& aMargin
,
161 const LogicalSize
& aBorder
,
162 const LogicalSize
& aPadding
,
165 nsIntSize size
= GetCanvasSize();
167 IntrinsicSize intrinsicSize
;
168 intrinsicSize
.width
.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(size
.width
));
169 intrinsicSize
.height
.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(size
.height
));
171 nsSize intrinsicRatio
= GetIntrinsicRatio(); // won't actually be used
173 return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(
175 aRenderingContext
, this,
176 intrinsicSize
, intrinsicRatio
,
184 nsHTMLCanvasFrame::Reflow(nsPresContext
* aPresContext
,
185 nsHTMLReflowMetrics
& aMetrics
,
186 const nsHTMLReflowState
& aReflowState
,
187 nsReflowStatus
& aStatus
)
189 DO_GLOBAL_REFLOW_COUNT("nsHTMLCanvasFrame");
190 DISPLAY_REFLOW(aPresContext
, this, aReflowState
, aMetrics
, aStatus
);
191 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS
,
192 ("enter nsHTMLCanvasFrame::Reflow: availSize=%d,%d",
193 aReflowState
.AvailableWidth(), aReflowState
.AvailableHeight()));
195 NS_PRECONDITION(mState
& NS_FRAME_IN_REFLOW
, "frame is not in reflow");
197 aStatus
= NS_FRAME_COMPLETE
;
199 WritingMode wm
= aReflowState
.GetWritingMode();
200 LogicalSize
finalSize(wm
,
201 aReflowState
.ComputedISize(),
202 aReflowState
.ComputedBSize());
204 // stash this away so we can compute our inner area later
205 mBorderPadding
= aReflowState
.ComputedLogicalBorderPadding();
207 finalSize
.ISize(wm
) += mBorderPadding
.IStartEnd(wm
);
208 finalSize
.BSize(wm
) += mBorderPadding
.BStartEnd(wm
);
210 if (GetPrevInFlow()) {
211 nscoord y
= GetContinuationOffset(&finalSize
.ISize(wm
));
212 finalSize
.BSize(wm
) -= y
+ mBorderPadding
.BStart(wm
);
213 finalSize
.BSize(wm
) = std::max(0, finalSize
.BSize(wm
));
216 aMetrics
.SetSize(wm
, finalSize
);
217 aMetrics
.SetOverflowAreasToDesiredBounds();
218 FinishAndStoreOverflow(&aMetrics
);
220 // Reflow the single anon block child.
221 nsReflowStatus childStatus
;
222 nsIFrame
* childFrame
= mFrames
.FirstChild();
223 WritingMode childWM
= childFrame
->GetWritingMode();
224 LogicalSize availSize
= aReflowState
.ComputedSize(childWM
);
225 availSize
.BSize(childWM
) = NS_UNCONSTRAINEDSIZE
;
226 NS_ASSERTION(!childFrame
->GetNextSibling(), "HTML canvas should have 1 kid");
227 nsHTMLReflowMetrics
childDesiredSize(aReflowState
.GetWritingMode(), aMetrics
.mFlags
);
228 nsHTMLReflowState
childReflowState(aPresContext
, aReflowState
, childFrame
,
230 ReflowChild(childFrame
, aPresContext
, childDesiredSize
, childReflowState
,
231 0, 0, 0, childStatus
, nullptr);
232 FinishReflowChild(childFrame
, aPresContext
, childDesiredSize
,
233 &childReflowState
, 0, 0, 0);
235 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS
,
236 ("exit nsHTMLCanvasFrame::Reflow: size=%d,%d",
237 aMetrics
.ISize(wm
), aMetrics
.BSize(wm
)));
238 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aMetrics
);
241 // FIXME taken from nsImageFrame, but then had splittable frame stuff
242 // removed. That needs to be fixed.
244 nsHTMLCanvasFrame::GetInnerArea() const
246 nsMargin bp
= mBorderPadding
.GetPhysicalMargin(GetWritingMode());
250 r
.width
= mRect
.width
- bp
.left
- bp
.right
;
251 r
.height
= mRect
.height
- bp
.top
- bp
.bottom
;
255 already_AddRefed
<Layer
>
256 nsHTMLCanvasFrame::BuildLayer(nsDisplayListBuilder
* aBuilder
,
257 LayerManager
* aManager
,
258 nsDisplayItem
* aItem
,
259 const ContainerLayerParameters
& aContainerParameters
)
261 nsRect area
= GetContentRect() - GetPosition() + aItem
->ToReferenceFrame();
262 HTMLCanvasElement
* element
= static_cast<HTMLCanvasElement
*>(GetContent());
263 nsIntSize canvasSize
= GetCanvasSize();
265 nsPresContext
* presContext
= PresContext();
266 element
->HandlePrintCallback(presContext
->Type());
268 if (canvasSize
.width
<= 0 || canvasSize
.height
<= 0 || area
.IsEmpty())
271 CanvasLayer
* oldLayer
= static_cast<CanvasLayer
*>
272 (aManager
->GetLayerBuilder()->GetLeafLayerFor(aBuilder
, aItem
));
273 nsRefPtr
<CanvasLayer
> layer
= element
->GetCanvasLayer(aBuilder
, oldLayer
, aManager
);
277 gfxRect r
= gfxRect(presContext
->AppUnitsToGfxUnits(area
.x
),
278 presContext
->AppUnitsToGfxUnits(area
.y
),
279 presContext
->AppUnitsToGfxUnits(area
.width
),
280 presContext
->AppUnitsToGfxUnits(area
.height
));
282 // Transform the canvas into the right place
283 gfx::Matrix transform
;
284 gfxPoint p
= r
.TopLeft() + aContainerParameters
.mOffset
;
285 transform
.Translate(p
.x
, p
.y
);
286 transform
.Scale(r
.Width()/canvasSize
.width
, r
.Height()/canvasSize
.height
);
287 layer
->SetBaseTransform(gfx::Matrix4x4::From2D(transform
));
288 layer
->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this));
290 return layer
.forget();
294 nsHTMLCanvasFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
295 const nsRect
& aDirtyRect
,
296 const nsDisplayListSet
& aLists
)
298 if (!IsVisibleForPainting(aBuilder
))
301 DisplayBorderBackgroundOutline(aBuilder
, aLists
);
303 DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
304 clip(aBuilder
, this, DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT
);
306 aLists
.Content()->AppendNewToTop(
307 new (aBuilder
) nsDisplayCanvas(aBuilder
, this));
309 DisplaySelectionOverlay(aBuilder
, aLists
.Content(),
310 nsISelectionDisplay::DISPLAY_IMAGES
);
314 nsHTMLCanvasFrame::GetType() const
316 return nsGkAtoms::HTMLCanvasFrame
;
319 // get the offset into the content area of the image where aImg starts if it is a continuation.
322 nsHTMLCanvasFrame::GetContinuationOffset(nscoord
* aWidth
) const
329 if (GetPrevInFlow()) {
330 for (nsIFrame
* prevInFlow
= GetPrevInFlow() ; prevInFlow
; prevInFlow
= prevInFlow
->GetPrevInFlow()) {
331 nsRect rect
= prevInFlow
->GetRect();
333 *aWidth
= rect
.width
;
335 offset
+= rect
.height
;
337 offset
-= mBorderPadding
.GetPhysicalMargin(GetWritingMode()).top
;
338 offset
= std::max(0, offset
);
345 nsHTMLCanvasFrame::AccessibleType()
347 return a11y::eHTMLCanvasType
;
351 #ifdef DEBUG_FRAME_DUMP
353 nsHTMLCanvasFrame::GetFrameName(nsAString
& aResult
) const
355 return MakeFrameName(NS_LITERAL_STRING("HTMLCanvas"), aResult
);