1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Vladimir Vukicevic <vladimir@pobox.com>
19 : * Portions created by the Initial Developer are Copyright (C) 2004
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Vladimir Vukicevic <vladimir@pobox.com> (original author)
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : /* rendering object for the HTML <canvas> element */
40 :
41 : #include "nsHTMLParts.h"
42 : #include "nsCOMPtr.h"
43 : #include "nsIServiceManager.h"
44 : #include "nsGkAtoms.h"
45 :
46 : #include "nsHTMLCanvasFrame.h"
47 : #include "nsHTMLCanvasElement.h"
48 : #include "nsDisplayList.h"
49 : #include "nsLayoutUtils.h"
50 :
51 : #include "nsTransform2D.h"
52 :
53 : #include "gfxContext.h"
54 :
55 : #ifdef ACCESSIBILITY
56 : #include "nsAccessibilityService.h"
57 : #endif
58 :
59 : using namespace mozilla;
60 : using namespace mozilla::layers;
61 :
62 : static nsHTMLCanvasElement *
63 0 : CanvasElementFromContent(nsIContent *content)
64 : {
65 0 : nsCOMPtr<nsIDOMHTMLCanvasElement> domCanvas(do_QueryInterface(content));
66 0 : return domCanvas ? static_cast<nsHTMLCanvasElement*>(domCanvas.get()) : nsnull;
67 : }
68 :
69 : class nsDisplayCanvas : public nsDisplayItem {
70 : public:
71 0 : nsDisplayCanvas(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
72 0 : : nsDisplayItem(aBuilder, aFrame)
73 : {
74 0 : MOZ_COUNT_CTOR(nsDisplayCanvas);
75 0 : }
76 : #ifdef NS_BUILD_REFCNT_LOGGING
77 0 : virtual ~nsDisplayCanvas() {
78 0 : MOZ_COUNT_DTOR(nsDisplayCanvas);
79 0 : }
80 : #endif
81 :
82 0 : NS_DISPLAY_DECL_NAME("nsDisplayCanvas", TYPE_CANVAS)
83 :
84 0 : virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
85 : bool* aForceTransparentSurface = nsnull) {
86 0 : if (aForceTransparentSurface) {
87 0 : *aForceTransparentSurface = false;
88 : }
89 0 : nsIFrame* f = GetUnderlyingFrame();
90 0 : nsHTMLCanvasElement *canvas = CanvasElementFromContent(f->GetContent());
91 0 : nsRegion result;
92 0 : if (canvas->GetIsOpaque()) {
93 0 : result = GetBounds(aBuilder);
94 : }
95 : return result;
96 : }
97 :
98 0 : virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) {
99 0 : nsHTMLCanvasFrame* f = static_cast<nsHTMLCanvasFrame*>(GetUnderlyingFrame());
100 0 : return f->GetInnerArea() + ToReferenceFrame();
101 : }
102 :
103 0 : virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
104 : LayerManager* aManager,
105 : const ContainerParameters& aContainerParameters)
106 : {
107 : return static_cast<nsHTMLCanvasFrame*>(mFrame)->
108 0 : BuildLayer(aBuilder, aManager, this);
109 : }
110 0 : virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
111 : LayerManager* aManager)
112 : {
113 0 : if (CanvasElementFromContent(mFrame->GetContent())->ShouldForceInactiveLayer(aManager))
114 0 : return LAYER_INACTIVE;
115 :
116 : // If compositing is cheap, just do that
117 0 : if (aManager->IsCompositingCheap())
118 0 : return mozilla::LAYER_ACTIVE;
119 :
120 0 : return mFrame->AreLayersMarkedActive() ? LAYER_ACTIVE : LAYER_INACTIVE;
121 : }
122 : };
123 :
124 :
125 : nsIFrame*
126 0 : NS_NewHTMLCanvasFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
127 : {
128 0 : return new (aPresShell) nsHTMLCanvasFrame(aContext);
129 : }
130 :
131 0 : NS_IMPL_FRAMEARENA_HELPERS(nsHTMLCanvasFrame)
132 :
133 : NS_IMETHODIMP
134 0 : nsHTMLCanvasFrame::Init(nsIContent* aContent,
135 : nsIFrame* aParent,
136 : nsIFrame* aPrevInFlow)
137 : {
138 0 : nsresult rv = nsSplittableFrame::Init(aContent, aParent, aPrevInFlow);
139 :
140 : // We can fill in the canvas before the canvas frame is created, in
141 : // which case we never get around to marking the layer active. Therefore,
142 : // we mark it active here when we create the frame.
143 0 : MarkLayersActive(nsChangeHint(0));
144 :
145 0 : return rv;
146 : }
147 :
148 0 : nsHTMLCanvasFrame::~nsHTMLCanvasFrame()
149 : {
150 0 : }
151 :
152 : nsIntSize
153 0 : nsHTMLCanvasFrame::GetCanvasSize()
154 : {
155 0 : nsIntSize size(0,0);
156 0 : nsHTMLCanvasElement *canvas = CanvasElementFromContent(GetContent());
157 0 : if (canvas) {
158 0 : size = canvas->GetSize();
159 : } else {
160 0 : NS_NOTREACHED("couldn't get canvas size");
161 : }
162 :
163 : return size;
164 : }
165 :
166 : /* virtual */ nscoord
167 0 : nsHTMLCanvasFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
168 : {
169 : // XXX The caller doesn't account for constraints of the height,
170 : // min-height, and max-height properties.
171 0 : nscoord result = nsPresContext::CSSPixelsToAppUnits(GetCanvasSize().width);
172 0 : DISPLAY_MIN_WIDTH(this, result);
173 0 : return result;
174 : }
175 :
176 : /* virtual */ nscoord
177 0 : nsHTMLCanvasFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
178 : {
179 : // XXX The caller doesn't account for constraints of the height,
180 : // min-height, and max-height properties.
181 0 : nscoord result = nsPresContext::CSSPixelsToAppUnits(GetCanvasSize().width);
182 0 : DISPLAY_PREF_WIDTH(this, result);
183 0 : return result;
184 : }
185 :
186 : /* virtual */ nsSize
187 0 : nsHTMLCanvasFrame::GetIntrinsicRatio()
188 : {
189 0 : nsIntSize size(GetCanvasSize());
190 : return nsSize(nsPresContext::CSSPixelsToAppUnits(size.width),
191 0 : nsPresContext::CSSPixelsToAppUnits(size.height));
192 : }
193 :
194 : /* virtual */ nsSize
195 0 : nsHTMLCanvasFrame::ComputeSize(nsRenderingContext *aRenderingContext,
196 : nsSize aCBSize, nscoord aAvailableWidth,
197 : nsSize aMargin, nsSize aBorder, nsSize aPadding,
198 : bool aShrinkWrap)
199 : {
200 0 : nsIntSize size = GetCanvasSize();
201 :
202 0 : IntrinsicSize intrinsicSize;
203 0 : intrinsicSize.width.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(size.width));
204 0 : intrinsicSize.height.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(size.height));
205 :
206 0 : nsSize intrinsicRatio = GetIntrinsicRatio(); // won't actually be used
207 :
208 : return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(
209 : aRenderingContext, this,
210 : intrinsicSize, intrinsicRatio, aCBSize,
211 0 : aMargin, aBorder, aPadding);
212 : }
213 :
214 : NS_IMETHODIMP
215 0 : nsHTMLCanvasFrame::Reflow(nsPresContext* aPresContext,
216 : nsHTMLReflowMetrics& aMetrics,
217 : const nsHTMLReflowState& aReflowState,
218 : nsReflowStatus& aStatus)
219 : {
220 0 : DO_GLOBAL_REFLOW_COUNT("nsHTMLCanvasFrame");
221 0 : DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
222 0 : NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
223 : ("enter nsHTMLCanvasFrame::Reflow: availSize=%d,%d",
224 : aReflowState.availableWidth, aReflowState.availableHeight));
225 :
226 0 : NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
227 :
228 0 : aStatus = NS_FRAME_COMPLETE;
229 :
230 0 : aMetrics.width = aReflowState.ComputedWidth();
231 0 : aMetrics.height = aReflowState.ComputedHeight();
232 :
233 : // stash this away so we can compute our inner area later
234 0 : mBorderPadding = aReflowState.mComputedBorderPadding;
235 :
236 0 : aMetrics.width += mBorderPadding.left + mBorderPadding.right;
237 0 : aMetrics.height += mBorderPadding.top + mBorderPadding.bottom;
238 :
239 0 : if (GetPrevInFlow()) {
240 0 : nscoord y = GetContinuationOffset(&aMetrics.width);
241 0 : aMetrics.height -= y + mBorderPadding.top;
242 0 : aMetrics.height = NS_MAX(0, aMetrics.height);
243 : }
244 :
245 0 : aMetrics.SetOverflowAreasToDesiredBounds();
246 0 : FinishAndStoreOverflow(&aMetrics);
247 :
248 0 : if (mRect.width != aMetrics.width || mRect.height != aMetrics.height) {
249 0 : Invalidate(nsRect(0, 0, mRect.width, mRect.height));
250 : }
251 :
252 : // Reflow the single anon block child.
253 : nsReflowStatus childStatus;
254 0 : nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE);
255 0 : nsIFrame* childFrame = mFrames.FirstChild();
256 0 : NS_ASSERTION(!childFrame->GetNextSibling(), "HTML canvas should have 1 kid");
257 0 : nsHTMLReflowMetrics childDesiredSize(aMetrics.mFlags);
258 : nsHTMLReflowState childReflowState(aPresContext, aReflowState, childFrame,
259 0 : availSize);
260 : ReflowChild(childFrame, aPresContext, childDesiredSize, childReflowState,
261 0 : 0, 0, 0, childStatus, nsnull);
262 : FinishReflowChild(childFrame, aPresContext, &childReflowState,
263 0 : childDesiredSize, 0, 0, 0);
264 :
265 0 : NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
266 : ("exit nsHTMLCanvasFrame::Reflow: size=%d,%d",
267 : aMetrics.width, aMetrics.height));
268 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
269 :
270 0 : return NS_OK;
271 : }
272 :
273 : // FIXME taken from nsImageFrame, but then had splittable frame stuff
274 : // removed. That needs to be fixed.
275 : nsRect
276 0 : nsHTMLCanvasFrame::GetInnerArea() const
277 : {
278 0 : nsRect r;
279 0 : r.x = mBorderPadding.left;
280 0 : r.y = mBorderPadding.top;
281 0 : r.width = mRect.width - mBorderPadding.left - mBorderPadding.right;
282 0 : r.height = mRect.height - mBorderPadding.top - mBorderPadding.bottom;
283 : return r;
284 : }
285 :
286 : already_AddRefed<Layer>
287 0 : nsHTMLCanvasFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
288 : LayerManager* aManager,
289 : nsDisplayItem* aItem)
290 : {
291 0 : nsRect area = GetContentRect() - GetPosition() + aItem->ToReferenceFrame();
292 0 : nsHTMLCanvasElement* element = static_cast<nsHTMLCanvasElement*>(GetContent());
293 0 : nsIntSize canvasSize = GetCanvasSize();
294 :
295 0 : if (canvasSize.width <= 0 || canvasSize.height <= 0 || area.IsEmpty())
296 0 : return nsnull;
297 :
298 : CanvasLayer* oldLayer = static_cast<CanvasLayer*>
299 0 : (aBuilder->LayerBuilder()->GetLeafLayerFor(aBuilder, aManager, aItem));
300 0 : nsRefPtr<CanvasLayer> layer = element->GetCanvasLayer(aBuilder, oldLayer, aManager);
301 0 : if (!layer)
302 0 : return nsnull;
303 :
304 0 : nsPresContext* presContext = PresContext();
305 : gfxRect r = gfxRect(presContext->AppUnitsToGfxUnits(area.x),
306 : presContext->AppUnitsToGfxUnits(area.y),
307 : presContext->AppUnitsToGfxUnits(area.width),
308 0 : presContext->AppUnitsToGfxUnits(area.height));
309 :
310 : // Transform the canvas into the right place
311 0 : gfxMatrix transform;
312 0 : transform.Translate(r.TopLeft());
313 0 : transform.Scale(r.Width()/canvasSize.width, r.Height()/canvasSize.height);
314 0 : layer->SetTransform(gfx3DMatrix::From2D(transform));
315 0 : layer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this));
316 0 : layer->SetVisibleRegion(nsIntRect(0, 0, canvasSize.width, canvasSize.height));
317 :
318 0 : return layer.forget();
319 : }
320 :
321 : NS_IMETHODIMP
322 0 : nsHTMLCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
323 : const nsRect& aDirtyRect,
324 : const nsDisplayListSet& aLists)
325 : {
326 0 : if (!IsVisibleForPainting(aBuilder))
327 0 : return NS_OK;
328 :
329 0 : nsresult rv = DisplayBorderBackgroundOutline(aBuilder, aLists);
330 0 : NS_ENSURE_SUCCESS(rv, rv);
331 :
332 0 : nsDisplayList replacedContent;
333 :
334 : rv = replacedContent.AppendNewToTop(
335 0 : new (aBuilder) nsDisplayCanvas(aBuilder, this));
336 0 : NS_ENSURE_SUCCESS(rv, rv);
337 :
338 : rv = DisplaySelectionOverlay(aBuilder, &replacedContent,
339 0 : nsISelectionDisplay::DISPLAY_IMAGES);
340 0 : NS_ENSURE_SUCCESS(rv, rv);
341 :
342 0 : WrapReplacedContentForBorderRadius(aBuilder, &replacedContent, aLists);
343 :
344 0 : return NS_OK;
345 : }
346 :
347 : nsIAtom*
348 0 : nsHTMLCanvasFrame::GetType() const
349 : {
350 0 : return nsGkAtoms::HTMLCanvasFrame;
351 : }
352 :
353 : // get the offset into the content area of the image where aImg starts if it is a continuation.
354 : // from nsImageFrame
355 : nscoord
356 0 : nsHTMLCanvasFrame::GetContinuationOffset(nscoord* aWidth) const
357 : {
358 0 : nscoord offset = 0;
359 0 : if (aWidth) {
360 0 : *aWidth = 0;
361 : }
362 :
363 0 : if (GetPrevInFlow()) {
364 0 : for (nsIFrame* prevInFlow = GetPrevInFlow() ; prevInFlow; prevInFlow = prevInFlow->GetPrevInFlow()) {
365 0 : nsRect rect = prevInFlow->GetRect();
366 0 : if (aWidth) {
367 0 : *aWidth = rect.width;
368 : }
369 0 : offset += rect.height;
370 : }
371 0 : offset -= mBorderPadding.top;
372 0 : offset = NS_MAX(0, offset);
373 : }
374 0 : return offset;
375 : }
376 :
377 : #ifdef ACCESSIBILITY
378 : already_AddRefed<nsAccessible>
379 0 : nsHTMLCanvasFrame::CreateAccessible()
380 : {
381 0 : nsAccessibilityService* accService = nsIPresShell::AccService();
382 0 : if (accService) {
383 0 : return accService->CreateHTMLCanvasAccessible(mContent, PresContext()->PresShell());
384 : }
385 0 : return nsnull;
386 : }
387 : #endif
388 :
389 : #ifdef DEBUG
390 : NS_IMETHODIMP
391 0 : nsHTMLCanvasFrame::GetFrameName(nsAString& aResult) const
392 : {
393 0 : return MakeFrameName(NS_LITERAL_STRING("HTMLCanvas"), aResult);
394 : }
395 : #endif
396 :
|