bug 480233. Make the display list item that canvas uses for painting know about the...
[mozilla-central.git] / layout / generic / nsHTMLCanvasFrame.cpp
blob17427c56589ea4505406608b8216be1388b0ceb5
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
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/
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.
15 * The Original Code is mozilla.org code.
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.
22 * Contributor(s):
23 * Vladimir Vukicevic <vladimir@pobox.com> (original author)
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.
37 * ***** END LICENSE BLOCK ***** */
39 /* rendering object for the HTML <canvas> element */
41 #include "nsHTMLParts.h"
42 #include "nsCOMPtr.h"
43 #include "nsIServiceManager.h"
44 #include "nsGkAtoms.h"
46 #include "nsHTMLCanvasFrame.h"
47 #include "nsICanvasElement.h"
48 #include "nsDisplayList.h"
50 #include "nsTransform2D.h"
53 class nsDisplayItemCanvas : public nsDisplayItem {
54 public:
55 nsDisplayItemCanvas(nsIFrame* aFrame)
56 : nsDisplayItem(aFrame)
58 MOZ_COUNT_CTOR(nsDisplayItemCanvas);
60 #ifdef NS_BUILD_REFCNT_LOGGING
61 virtual ~nsDisplayItemCanvas() {
62 MOZ_COUNT_DTOR(nsDisplayItemCanvas);
64 #endif
66 NS_DISPLAY_DECL_NAME("nsDisplayItemCanvas")
68 virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx,
69 const nsRect& aDirtyRect) {
70 nsHTMLCanvasFrame* f = static_cast<nsHTMLCanvasFrame*>(GetUnderlyingFrame());
71 f->PaintCanvas(*aCtx, aDirtyRect, aBuilder->ToReferenceFrame(f));
74 virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder) {
75 nsIFrame* f = GetUnderlyingFrame();
76 nsCOMPtr<nsICanvasElement> canvas(do_QueryInterface(f->GetContent()));
77 return canvas->GetIsOpaque();
80 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) {
81 nsHTMLCanvasFrame* f = static_cast<nsHTMLCanvasFrame*>(GetUnderlyingFrame());
82 return f->GetInnerArea() + aBuilder->ToReferenceFrame(f);
87 nsIFrame*
88 NS_NewHTMLCanvasFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
90 return new (aPresShell) nsHTMLCanvasFrame(aContext);
93 nsHTMLCanvasFrame::~nsHTMLCanvasFrame()
97 nsIntSize
98 nsHTMLCanvasFrame::GetCanvasSize()
100 PRUint32 w, h;
101 nsresult rv;
102 nsCOMPtr<nsICanvasElement> canvas(do_QueryInterface(GetContent()));
103 if (canvas) {
104 rv = canvas->GetSize(&w, &h);
105 } else {
106 rv = NS_ERROR_NULL_POINTER;
109 if (NS_FAILED(rv)) {
110 NS_NOTREACHED("couldn't get canvas size");
111 h = w = 1;
114 return nsIntSize(w, h);
117 /* virtual */ nscoord
118 nsHTMLCanvasFrame::GetMinWidth(nsIRenderingContext *aRenderingContext)
120 // XXX The caller doesn't account for constraints of the height,
121 // min-height, and max-height properties.
122 nscoord result = nsPresContext::CSSPixelsToAppUnits(GetCanvasSize().width);
123 DISPLAY_MIN_WIDTH(this, result);
124 return result;
127 /* virtual */ nscoord
128 nsHTMLCanvasFrame::GetPrefWidth(nsIRenderingContext *aRenderingContext)
130 // XXX The caller doesn't account for constraints of the height,
131 // min-height, and max-height properties.
132 nscoord result = nsPresContext::CSSPixelsToAppUnits(GetCanvasSize().width);
133 DISPLAY_PREF_WIDTH(this, result);
134 return result;
137 /* virtual */ nsSize
138 nsHTMLCanvasFrame::GetIntrinsicRatio()
140 nsIntSize size(GetCanvasSize());
141 return nsSize(nsPresContext::CSSPixelsToAppUnits(size.width),
142 nsPresContext::CSSPixelsToAppUnits(size.height));
145 /* virtual */ nsSize
146 nsHTMLCanvasFrame::ComputeSize(nsIRenderingContext *aRenderingContext,
147 nsSize aCBSize, nscoord aAvailableWidth,
148 nsSize aMargin, nsSize aBorder, nsSize aPadding,
149 PRBool aShrinkWrap)
151 nsIntSize size = GetCanvasSize();
153 IntrinsicSize intrinsicSize;
154 intrinsicSize.width.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(size.width));
155 intrinsicSize.height.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(size.height));
157 nsSize intrinsicRatio = GetIntrinsicRatio(); // won't actually be used
159 return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(
160 aRenderingContext, this,
161 intrinsicSize, intrinsicRatio, aCBSize,
162 aMargin, aBorder, aPadding);
165 NS_IMETHODIMP
166 nsHTMLCanvasFrame::Reflow(nsPresContext* aPresContext,
167 nsHTMLReflowMetrics& aMetrics,
168 const nsHTMLReflowState& aReflowState,
169 nsReflowStatus& aStatus)
171 DO_GLOBAL_REFLOW_COUNT("nsHTMLCanvasFrame");
172 DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
173 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
174 ("enter nsHTMLCanvasFrame::Reflow: availSize=%d,%d",
175 aReflowState.availableWidth, aReflowState.availableHeight));
177 NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
179 aStatus = NS_FRAME_COMPLETE;
181 aMetrics.width = aReflowState.ComputedWidth();
182 aMetrics.height = aReflowState.ComputedHeight();
184 // stash this away so we can compute our inner area later
185 mBorderPadding = aReflowState.mComputedBorderPadding;
187 aMetrics.width += mBorderPadding.left + mBorderPadding.right;
188 aMetrics.height += mBorderPadding.top + mBorderPadding.bottom;
190 if (GetPrevInFlow()) {
191 nscoord y = GetContinuationOffset(&aMetrics.width);
192 aMetrics.height -= y + mBorderPadding.top;
193 aMetrics.height = PR_MAX(0, aMetrics.height);
196 aMetrics.mOverflowArea.SetRect(0, 0, aMetrics.width, aMetrics.height);
197 FinishAndStoreOverflow(&aMetrics);
199 if (mRect.width != aMetrics.width || mRect.height != aMetrics.height) {
200 Invalidate(nsRect(0, 0, mRect.width, mRect.height));
203 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
204 ("exit nsHTMLCanvasFrame::Reflow: size=%d,%d",
205 aMetrics.width, aMetrics.height));
206 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
207 return NS_OK;
210 // FIXME taken from nsImageFrame, but then had splittable frame stuff
211 // removed. That needs to be fixed.
212 nsRect
213 nsHTMLCanvasFrame::GetInnerArea() const
215 nsRect r;
216 r.x = mBorderPadding.left;
217 r.y = mBorderPadding.top;
218 r.width = mRect.width - mBorderPadding.left - mBorderPadding.right;
219 r.height = mRect.height - mBorderPadding.top - mBorderPadding.bottom;
220 return r;
223 void
224 nsHTMLCanvasFrame::PaintCanvas(nsIRenderingContext& aRenderingContext,
225 const nsRect& aDirtyRect, nsPoint aPt)
227 nsRect inner = GetInnerArea() + aPt;
229 nsCOMPtr<nsICanvasElement> canvas(do_QueryInterface(GetContent()));
230 if (!canvas)
231 return;
233 // anything to do?
234 if (inner.width == 0 || inner.height == 0)
235 return;
237 nsIntSize canvasSize = GetCanvasSize();
238 nsSize sizeAppUnits(PresContext()->DevPixelsToAppUnits(canvasSize.width),
239 PresContext()->DevPixelsToAppUnits(canvasSize.height));
241 // XXXvlad clip to aDirtyRect!
243 if (inner.Size() != sizeAppUnits)
245 float sx = inner.width / (float) sizeAppUnits.width;
246 float sy = inner.height / (float) sizeAppUnits.height;
248 aRenderingContext.PushState();
249 aRenderingContext.Translate(inner.x, inner.y);
250 aRenderingContext.Scale(sx, sy);
252 canvas->RenderContexts(aRenderingContext.ThebesContext());
254 aRenderingContext.PopState();
255 } else {
256 //nsIRenderingContext::AutoPushTranslation(&aRenderingContext, px, py);
258 aRenderingContext.PushState();
259 aRenderingContext.Translate(inner.x, inner.y);
261 canvas->RenderContexts(aRenderingContext.ThebesContext());
263 aRenderingContext.PopState();
267 NS_IMETHODIMP
268 nsHTMLCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
269 const nsRect& aDirtyRect,
270 const nsDisplayListSet& aLists)
272 if (!IsVisibleForPainting(aBuilder))
273 return NS_OK;
275 nsresult rv = DisplayBorderBackgroundOutline(aBuilder, aLists);
276 NS_ENSURE_SUCCESS(rv, rv);
278 rv = aLists.Content()->AppendNewToTop(new (aBuilder)
279 nsDisplayItemCanvas(this));
280 NS_ENSURE_SUCCESS(rv, rv);
282 return DisplaySelectionOverlay(aBuilder, aLists,
283 nsISelectionDisplay::DISPLAY_IMAGES);
286 NS_IMETHODIMP
287 nsHTMLCanvasFrame::GetContentForEvent(nsPresContext* aPresContext,
288 nsEvent* aEvent,
289 nsIContent** aContent)
291 NS_ENSURE_ARG_POINTER(aContent);
292 *aContent = GetContent();
293 NS_IF_ADDREF(*aContent);
294 return NS_OK;
297 nsIAtom*
298 nsHTMLCanvasFrame::GetType() const
300 return nsGkAtoms::HTMLCanvasFrame;
303 // get the offset into the content area of the image where aImg starts if it is a continuation.
304 // from nsImageFrame
305 nscoord
306 nsHTMLCanvasFrame::GetContinuationOffset(nscoord* aWidth) const
308 nscoord offset = 0;
309 if (aWidth) {
310 *aWidth = 0;
313 if (GetPrevInFlow()) {
314 for (nsIFrame* prevInFlow = GetPrevInFlow() ; prevInFlow; prevInFlow = prevInFlow->GetPrevInFlow()) {
315 nsRect rect = prevInFlow->GetRect();
316 if (aWidth) {
317 *aWidth = rect.width;
319 offset += rect.height;
321 offset -= mBorderPadding.top;
322 offset = PR_MAX(0, offset);
324 return offset;
327 #ifdef ACCESSIBILITY
328 NS_IMETHODIMP
329 nsHTMLCanvasFrame::GetAccessible(nsIAccessible** aAccessible)
331 return NS_ERROR_FAILURE;
333 #endif
335 #ifdef DEBUG
336 NS_IMETHODIMP
337 nsHTMLCanvasFrame::GetFrameName(nsAString& aResult) const
339 return MakeFrameName(NS_LITERAL_STRING("HTMLCanvas"), aResult);
342 NS_IMETHODIMP
343 nsHTMLCanvasFrame::List(FILE* out, PRInt32 aIndent) const
345 IndentBy(out, aIndent);
346 ListTag(out);
347 fputs("\n", out);
348 return NS_OK;
350 #endif