|
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 for the HTML <canvas> element */ |
|
7 |
|
8 #include "nsHTMLCanvasFrame.h" |
|
9 |
|
10 #include "nsGkAtoms.h" |
|
11 #include "mozilla/dom/HTMLCanvasElement.h" |
|
12 #include "nsDisplayList.h" |
|
13 #include "nsLayoutUtils.h" |
|
14 #include "Layers.h" |
|
15 #include "ActiveLayerTracker.h" |
|
16 |
|
17 #include <algorithm> |
|
18 |
|
19 using namespace mozilla; |
|
20 using namespace mozilla::dom; |
|
21 using namespace mozilla::layers; |
|
22 |
|
23 class nsDisplayCanvas : public nsDisplayItem { |
|
24 public: |
|
25 nsDisplayCanvas(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) |
|
26 : nsDisplayItem(aBuilder, aFrame) |
|
27 { |
|
28 MOZ_COUNT_CTOR(nsDisplayCanvas); |
|
29 } |
|
30 #ifdef NS_BUILD_REFCNT_LOGGING |
|
31 virtual ~nsDisplayCanvas() { |
|
32 MOZ_COUNT_DTOR(nsDisplayCanvas); |
|
33 } |
|
34 #endif |
|
35 |
|
36 NS_DISPLAY_DECL_NAME("nsDisplayCanvas", TYPE_CANVAS) |
|
37 |
|
38 virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, |
|
39 bool* aSnap) MOZ_OVERRIDE { |
|
40 *aSnap = false; |
|
41 nsIFrame* f = Frame(); |
|
42 HTMLCanvasElement *canvas = |
|
43 HTMLCanvasElement::FromContent(f->GetContent()); |
|
44 nsRegion result; |
|
45 if (canvas->GetIsOpaque()) { |
|
46 result = GetBounds(aBuilder, aSnap); |
|
47 } |
|
48 return result; |
|
49 } |
|
50 |
|
51 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, |
|
52 bool* aSnap) MOZ_OVERRIDE { |
|
53 *aSnap = true; |
|
54 nsHTMLCanvasFrame* f = static_cast<nsHTMLCanvasFrame*>(Frame()); |
|
55 return f->GetInnerArea() + ToReferenceFrame(); |
|
56 } |
|
57 |
|
58 virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder, |
|
59 LayerManager* aManager, |
|
60 const ContainerLayerParameters& aContainerParameters) MOZ_OVERRIDE |
|
61 { |
|
62 return static_cast<nsHTMLCanvasFrame*>(mFrame)-> |
|
63 BuildLayer(aBuilder, aManager, this, aContainerParameters); |
|
64 } |
|
65 virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder, |
|
66 LayerManager* aManager, |
|
67 const ContainerLayerParameters& aParameters) MOZ_OVERRIDE |
|
68 { |
|
69 if (HTMLCanvasElement::FromContent(mFrame->GetContent())->ShouldForceInactiveLayer(aManager)) |
|
70 return LAYER_INACTIVE; |
|
71 |
|
72 // If compositing is cheap, just do that |
|
73 if (aManager->IsCompositingCheap() || |
|
74 ActiveLayerTracker::IsContentActive(mFrame)) |
|
75 return mozilla::LAYER_ACTIVE; |
|
76 |
|
77 return LAYER_INACTIVE; |
|
78 } |
|
79 }; |
|
80 |
|
81 |
|
82 nsIFrame* |
|
83 NS_NewHTMLCanvasFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) |
|
84 { |
|
85 return new (aPresShell) nsHTMLCanvasFrame(aContext); |
|
86 } |
|
87 |
|
88 NS_QUERYFRAME_HEAD(nsHTMLCanvasFrame) |
|
89 NS_QUERYFRAME_ENTRY(nsHTMLCanvasFrame) |
|
90 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) |
|
91 |
|
92 NS_IMPL_FRAMEARENA_HELPERS(nsHTMLCanvasFrame) |
|
93 |
|
94 void |
|
95 nsHTMLCanvasFrame::Init(nsIContent* aContent, |
|
96 nsIFrame* aParent, |
|
97 nsIFrame* aPrevInFlow) |
|
98 { |
|
99 nsContainerFrame::Init(aContent, aParent, aPrevInFlow); |
|
100 |
|
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); |
|
105 } |
|
106 |
|
107 nsHTMLCanvasFrame::~nsHTMLCanvasFrame() |
|
108 { |
|
109 } |
|
110 |
|
111 nsIntSize |
|
112 nsHTMLCanvasFrame::GetCanvasSize() |
|
113 { |
|
114 nsIntSize size(0,0); |
|
115 HTMLCanvasElement *canvas = |
|
116 HTMLCanvasElement::FromContentOrNull(GetContent()); |
|
117 if (canvas) { |
|
118 size = canvas->GetSize(); |
|
119 } else { |
|
120 NS_NOTREACHED("couldn't get canvas size"); |
|
121 } |
|
122 |
|
123 return size; |
|
124 } |
|
125 |
|
126 /* virtual */ nscoord |
|
127 nsHTMLCanvasFrame::GetMinWidth(nsRenderingContext *aRenderingContext) |
|
128 { |
|
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); |
|
133 return result; |
|
134 } |
|
135 |
|
136 /* virtual */ nscoord |
|
137 nsHTMLCanvasFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) |
|
138 { |
|
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); |
|
143 return result; |
|
144 } |
|
145 |
|
146 /* virtual */ nsSize |
|
147 nsHTMLCanvasFrame::GetIntrinsicRatio() |
|
148 { |
|
149 nsIntSize size(GetCanvasSize()); |
|
150 return nsSize(nsPresContext::CSSPixelsToAppUnits(size.width), |
|
151 nsPresContext::CSSPixelsToAppUnits(size.height)); |
|
152 } |
|
153 |
|
154 /* virtual */ nsSize |
|
155 nsHTMLCanvasFrame::ComputeSize(nsRenderingContext *aRenderingContext, |
|
156 nsSize aCBSize, nscoord aAvailableWidth, |
|
157 nsSize aMargin, nsSize aBorder, nsSize aPadding, |
|
158 uint32_t aFlags) |
|
159 { |
|
160 nsIntSize size = GetCanvasSize(); |
|
161 |
|
162 IntrinsicSize intrinsicSize; |
|
163 intrinsicSize.width.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(size.width)); |
|
164 intrinsicSize.height.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(size.height)); |
|
165 |
|
166 nsSize intrinsicRatio = GetIntrinsicRatio(); // won't actually be used |
|
167 |
|
168 return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions( |
|
169 aRenderingContext, this, |
|
170 intrinsicSize, intrinsicRatio, aCBSize, |
|
171 aMargin, aBorder, aPadding); |
|
172 } |
|
173 |
|
174 nsresult |
|
175 nsHTMLCanvasFrame::Reflow(nsPresContext* aPresContext, |
|
176 nsHTMLReflowMetrics& aMetrics, |
|
177 const nsHTMLReflowState& aReflowState, |
|
178 nsReflowStatus& aStatus) |
|
179 { |
|
180 DO_GLOBAL_REFLOW_COUNT("nsHTMLCanvasFrame"); |
|
181 DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus); |
|
182 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, |
|
183 ("enter nsHTMLCanvasFrame::Reflow: availSize=%d,%d", |
|
184 aReflowState.AvailableWidth(), aReflowState.AvailableHeight())); |
|
185 |
|
186 NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow"); |
|
187 |
|
188 aStatus = NS_FRAME_COMPLETE; |
|
189 |
|
190 aMetrics.Width() = aReflowState.ComputedWidth(); |
|
191 aMetrics.Height() = aReflowState.ComputedHeight(); |
|
192 |
|
193 // stash this away so we can compute our inner area later |
|
194 mBorderPadding = aReflowState.ComputedPhysicalBorderPadding(); |
|
195 |
|
196 aMetrics.Width() += mBorderPadding.left + mBorderPadding.right; |
|
197 aMetrics.Height() += mBorderPadding.top + mBorderPadding.bottom; |
|
198 |
|
199 if (GetPrevInFlow()) { |
|
200 nscoord y = GetContinuationOffset(&aMetrics.Width()); |
|
201 aMetrics.Height() -= y + mBorderPadding.top; |
|
202 aMetrics.Height() = std::max(0, aMetrics.Height()); |
|
203 } |
|
204 |
|
205 aMetrics.SetOverflowAreasToDesiredBounds(); |
|
206 FinishAndStoreOverflow(&aMetrics); |
|
207 |
|
208 // Reflow the single anon block child. |
|
209 nsReflowStatus childStatus; |
|
210 nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE); |
|
211 nsIFrame* childFrame = mFrames.FirstChild(); |
|
212 NS_ASSERTION(!childFrame->GetNextSibling(), "HTML canvas should have 1 kid"); |
|
213 nsHTMLReflowMetrics childDesiredSize(aReflowState.GetWritingMode(), aMetrics.mFlags); |
|
214 nsHTMLReflowState childReflowState(aPresContext, aReflowState, childFrame, |
|
215 availSize); |
|
216 ReflowChild(childFrame, aPresContext, childDesiredSize, childReflowState, |
|
217 0, 0, 0, childStatus, nullptr); |
|
218 FinishReflowChild(childFrame, aPresContext, childDesiredSize, |
|
219 &childReflowState, 0, 0, 0); |
|
220 |
|
221 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, |
|
222 ("exit nsHTMLCanvasFrame::Reflow: size=%d,%d", |
|
223 aMetrics.Width(), aMetrics.Height())); |
|
224 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics); |
|
225 |
|
226 return NS_OK; |
|
227 } |
|
228 |
|
229 // FIXME taken from nsImageFrame, but then had splittable frame stuff |
|
230 // removed. That needs to be fixed. |
|
231 nsRect |
|
232 nsHTMLCanvasFrame::GetInnerArea() const |
|
233 { |
|
234 nsRect r; |
|
235 r.x = mBorderPadding.left; |
|
236 r.y = mBorderPadding.top; |
|
237 r.width = mRect.width - mBorderPadding.left - mBorderPadding.right; |
|
238 r.height = mRect.height - mBorderPadding.top - mBorderPadding.bottom; |
|
239 return r; |
|
240 } |
|
241 |
|
242 already_AddRefed<Layer> |
|
243 nsHTMLCanvasFrame::BuildLayer(nsDisplayListBuilder* aBuilder, |
|
244 LayerManager* aManager, |
|
245 nsDisplayItem* aItem, |
|
246 const ContainerLayerParameters& aContainerParameters) |
|
247 { |
|
248 nsRect area = GetContentRect() - GetPosition() + aItem->ToReferenceFrame(); |
|
249 HTMLCanvasElement* element = static_cast<HTMLCanvasElement*>(GetContent()); |
|
250 nsIntSize canvasSize = GetCanvasSize(); |
|
251 |
|
252 nsPresContext* presContext = PresContext(); |
|
253 element->HandlePrintCallback(presContext->Type()); |
|
254 |
|
255 if (canvasSize.width <= 0 || canvasSize.height <= 0 || area.IsEmpty()) |
|
256 return nullptr; |
|
257 |
|
258 CanvasLayer* oldLayer = static_cast<CanvasLayer*> |
|
259 (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aItem)); |
|
260 nsRefPtr<CanvasLayer> layer = element->GetCanvasLayer(aBuilder, oldLayer, aManager); |
|
261 if (!layer) |
|
262 return nullptr; |
|
263 |
|
264 gfxRect r = gfxRect(presContext->AppUnitsToGfxUnits(area.x), |
|
265 presContext->AppUnitsToGfxUnits(area.y), |
|
266 presContext->AppUnitsToGfxUnits(area.width), |
|
267 presContext->AppUnitsToGfxUnits(area.height)); |
|
268 |
|
269 // Transform the canvas into the right place |
|
270 gfx::Matrix transform; |
|
271 gfxPoint p = r.TopLeft() + aContainerParameters.mOffset; |
|
272 transform.Translate(p.x, p.y); |
|
273 transform.Scale(r.Width()/canvasSize.width, r.Height()/canvasSize.height); |
|
274 layer->SetBaseTransform(gfx::Matrix4x4::From2D(transform)); |
|
275 layer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this)); |
|
276 layer->SetVisibleRegion(nsIntRect(0, 0, canvasSize.width, canvasSize.height)); |
|
277 |
|
278 return layer.forget(); |
|
279 } |
|
280 |
|
281 void |
|
282 nsHTMLCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
|
283 const nsRect& aDirtyRect, |
|
284 const nsDisplayListSet& aLists) |
|
285 { |
|
286 if (!IsVisibleForPainting(aBuilder)) |
|
287 return; |
|
288 |
|
289 DisplayBorderBackgroundOutline(aBuilder, aLists); |
|
290 |
|
291 DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox |
|
292 clip(aBuilder, this, DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT); |
|
293 |
|
294 aLists.Content()->AppendNewToTop( |
|
295 new (aBuilder) nsDisplayCanvas(aBuilder, this)); |
|
296 |
|
297 DisplaySelectionOverlay(aBuilder, aLists.Content(), |
|
298 nsISelectionDisplay::DISPLAY_IMAGES); |
|
299 } |
|
300 |
|
301 nsIAtom* |
|
302 nsHTMLCanvasFrame::GetType() const |
|
303 { |
|
304 return nsGkAtoms::HTMLCanvasFrame; |
|
305 } |
|
306 |
|
307 // get the offset into the content area of the image where aImg starts if it is a continuation. |
|
308 // from nsImageFrame |
|
309 nscoord |
|
310 nsHTMLCanvasFrame::GetContinuationOffset(nscoord* aWidth) const |
|
311 { |
|
312 nscoord offset = 0; |
|
313 if (aWidth) { |
|
314 *aWidth = 0; |
|
315 } |
|
316 |
|
317 if (GetPrevInFlow()) { |
|
318 for (nsIFrame* prevInFlow = GetPrevInFlow() ; prevInFlow; prevInFlow = prevInFlow->GetPrevInFlow()) { |
|
319 nsRect rect = prevInFlow->GetRect(); |
|
320 if (aWidth) { |
|
321 *aWidth = rect.width; |
|
322 } |
|
323 offset += rect.height; |
|
324 } |
|
325 offset -= mBorderPadding.top; |
|
326 offset = std::max(0, offset); |
|
327 } |
|
328 return offset; |
|
329 } |
|
330 |
|
331 #ifdef ACCESSIBILITY |
|
332 a11y::AccType |
|
333 nsHTMLCanvasFrame::AccessibleType() |
|
334 { |
|
335 return a11y::eHTMLCanvasType; |
|
336 } |
|
337 #endif |
|
338 |
|
339 #ifdef DEBUG_FRAME_DUMP |
|
340 nsresult |
|
341 nsHTMLCanvasFrame::GetFrameName(nsAString& aResult) const |
|
342 { |
|
343 return MakeFrameName(NS_LITERAL_STRING("HTMLCanvas"), aResult); |
|
344 } |
|
345 #endif |
|
346 |