Back to index

lightning-sunbird  0.9+nobinonly
nsCanvasRenderingContext2D.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  *   Vladimir Vukicevic <vladimir@pobox.com>
00019  * Portions created by the Initial Developer are Copyright (C) 2005
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #ifdef _MSC_VER
00039 #define _USE_MATH_DEFINES
00040 #endif
00041 #include <math.h>
00042 
00043 #include "prmem.h"
00044 
00045 #include "nsIServiceManager.h"
00046 
00047 #include "nsContentUtils.h"
00048 
00049 #include "nsIDOMDocument.h"
00050 #include "nsIDocument.h"
00051 #include "nsIDOMCanvasRenderingContext2D.h"
00052 #include "nsICanvasRenderingContextInternal.h"
00053 #include "nsPresContext.h"
00054 #include "nsIPresShell.h"
00055 #include "nsIVariant.h"
00056 
00057 #ifdef MOZILLA_1_8_BRANCH
00058 #include "nsIScriptGlobalObject.h"
00059 #include "nsIViewManager.h"
00060 #include "nsIScrollableView.h"
00061 #endif
00062 
00063 #include "imgIRequest.h"
00064 #include "imgIContainer.h"
00065 #include "gfxIImageFrame.h"
00066 #include "nsIDOMHTMLCanvasElement.h"
00067 #include "nsICanvasElement.h"
00068 #include "nsIDOMHTMLImageElement.h"
00069 #include "nsIImageLoadingContent.h"
00070 #include "nsIInterfaceRequestorUtils.h"
00071 #include "nsIImage.h"
00072 #include "nsIFrame.h"
00073 #include "nsDOMError.h"
00074 
00075 #include "nsICSSParser.h"
00076 
00077 #include "nsPrintfCString.h"
00078 
00079 #include "nsReadableUtils.h"
00080 
00081 #include "nsColor.h"
00082 #include "nsTransform2D.h"
00083 #include "nsIRenderingContext.h"
00084 #include "nsIDeviceContext.h"
00085 #include "nsIBlender.h"
00086 #include "nsGfxCIID.h"
00087 #include "nsIDrawingSurface.h"
00088 #include "nsIScriptSecurityManager.h"
00089 #include "nsIDocShell.h"
00090 #include "nsPresContext.h"
00091 #include "nsIPresShell.h"
00092 #include "nsIDOMWindow.h"
00093 #include "nsPIDOMWindow.h"
00094 #include "nsIXPConnect.h"
00095 #include "jsapi.h"
00096 #include "jsnum.h"
00097 
00098 #include "nsTArray.h"
00099 
00100 #include "cairo.h"
00101 #include "imgIEncoder.h"
00102 #ifdef MOZILLA_1_8_BRANCH
00103 #define imgIEncoder imgIEncoder_MOZILLA_1_8_BRANCH
00104 #endif
00105 
00106 #ifdef MOZ_CAIRO_GFX
00107 #include "gfxContext.h"
00108 #include "gfxASurface.h"
00109 #include "gfxPlatform.h"
00110 
00111 #include "nsDisplayList.h"
00112 #include "nsIViewManager.h"
00113 #include "nsIScrollableView.h"
00114 #include "nsFrameManager.h"
00115 #include "nsRegion.h"
00116 #endif
00117 
00118 #ifdef XP_WIN
00119 #include "cairo-win32.h"
00120 
00121 #ifdef MOZILLA_1_8_BRANCH
00122 extern "C" {
00123 cairo_surface_t *
00124 _cairo_win32_surface_create_dib (cairo_format_t format,
00125                                  int              width,
00126                                  int              height);
00127 }
00128 #endif
00129 
00130 #ifndef M_PI
00131 #define M_PI         3.14159265358979323846
00132 #define M_PI_2              1.57079632679489661923
00133 #endif
00134 #endif
00135 
00136 #ifdef XP_OS2
00137 #define INCL_WINWINDOWMGR
00138 #define INCL_GPIBITMAPS
00139 #include <os2.h>
00140 #include "nsDrawingSurfaceOS2.h"
00141 #include "cairo-os2.h"
00142 #endif
00143 
00144 #ifdef MOZ_WIDGET_GTK2
00145 #include "cairo-xlib.h"
00146 #include "cairo-xlib-xrender.h"
00147 #include <gdk/gdk.h>
00148 #include <gdk/gdkx.h>
00149 #endif
00150 
00151 #ifdef XP_MACOSX
00152 #include <Quickdraw.h>
00153 #include <CGContext.h>
00154 
00155 /* This has to be 0 on machines less than 10.4 (which are always PPC);
00156  * otherwise the older CG gets confused if we pass in
00157  * kCGBitmapByteOrder32Big since it doesn't understand it.
00158  */
00159 #ifdef __BIG_ENDIAN__
00160 #define CG_BITMAP_BYTE_ORDER_FLAG 0
00161 #else    /* Little endian. */
00162 /* x86, and will be a 10.4u SDK; ByteOrder32Host will be defined */
00163 #define CG_BITMAP_BYTE_ORDER_FLAG kCGBitmapByteOrder32Host
00164 #endif
00165 
00166 #include "nsDrawingSurfaceMac.h"
00167 
00168 #endif
00169 
00170 static NS_DEFINE_IID(kBlenderCID, NS_BLENDER_CID);
00171 
00172 /* Maximum depth of save() which has style information saved */
00173 #define STYLE_STACK_DEPTH 50
00174 #define STYLE_CURRENT_STACK ((mSaveCount<STYLE_STACK_DEPTH)?mSaveCount:STYLE_STACK_DEPTH-1)
00175 
00176 static PRBool CheckSaneImageSize (PRInt32 width, PRInt32 height);
00177 static PRBool CheckSaneSubrectSize (PRInt32 x, PRInt32 y, PRInt32 w, PRInt32 h, PRInt32 realWidth, PRInt32 realHeight);
00178 
00179 /* Float validation stuff */
00180 
00181 #define VALIDATE(_f)  if (!JSDOUBLE_IS_FINITE(_f)) return PR_FALSE
00182 
00183 /* These must take doubles as args, because JSDOUBLE_IS_FINITE expects
00184  * to take the address of its argument; we can't cast/convert in the
00185  * macro.
00186  */
00187 
00188 static PRBool FloatValidate (double f1) {
00189     VALIDATE(f1);
00190     return PR_TRUE;
00191 }
00192 
00193 static PRBool FloatValidate (double f1, double f2) {
00194     VALIDATE(f1); VALIDATE(f2);
00195     return PR_TRUE;
00196 }
00197 
00198 static PRBool FloatValidate (double f1, double f2, double f3) {
00199     VALIDATE(f1); VALIDATE(f2); VALIDATE(f3);
00200     return PR_TRUE;
00201 }
00202 
00203 static PRBool FloatValidate (double f1, double f2, double f3, double f4) {
00204     VALIDATE(f1); VALIDATE(f2); VALIDATE(f3); VALIDATE(f4);
00205     return PR_TRUE;
00206 }
00207 
00208 static PRBool FloatValidate (double f1, double f2, double f3, double f4, double f5) {
00209     VALIDATE(f1); VALIDATE(f2); VALIDATE(f3); VALIDATE(f4); VALIDATE(f5);
00210     return PR_TRUE;
00211 }
00212 
00213 static PRBool FloatValidate (double f1, double f2, double f3, double f4, double f5, double f6) {
00214     VALIDATE(f1); VALIDATE(f2); VALIDATE(f3); VALIDATE(f4); VALIDATE(f5); VALIDATE(f6);
00215     return PR_TRUE;
00216 }
00217 
00218 #undef VALIDATE
00219 
00223 #define NS_CANVASGRADIENT_PRIVATE_IID \
00224     { 0x491d39d8, 0x4058, 0x42bd, { 0xac, 0x76, 0x70, 0xd5, 0x62, 0x7f, 0x02, 0x10 } }
00225 class nsCanvasGradient : public nsIDOMCanvasGradient
00226 {
00227 public:
00228 #ifdef MOZILLA_1_8_BRANCH
00229     NS_DEFINE_STATIC_IID_ACCESSOR(NS_CANVASGRADIENT_PRIVATE_IID)
00230 #else
00231     NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASGRADIENT_PRIVATE_IID)
00232 #endif
00233 
00234     nsCanvasGradient(cairo_pattern_t *cpat, nsICSSParser *cssparser)
00235         : mPattern(cpat), mCSSParser(cssparser)
00236     {
00237     }
00238 
00239     ~nsCanvasGradient() {
00240         if (mPattern)
00241             cairo_pattern_destroy(mPattern);
00242     }
00243 
00244     void Apply(cairo_t *cairo) {
00245         cairo_set_source(cairo, mPattern);
00246     }
00247 
00248     /* nsIDOMCanvasGradient */
00249     NS_IMETHOD AddColorStop (float offset,
00250                              const nsAString& colorstr)
00251     {
00252         nscolor color;
00253 
00254         if (!FloatValidate(offset))
00255             return NS_ERROR_DOM_SYNTAX_ERR;
00256 
00257         if (offset < 0.0 || offset > 1.0)
00258             return NS_ERROR_DOM_INDEX_SIZE_ERR;
00259 
00260         nsresult rv = mCSSParser->ParseColorString(nsString(colorstr), nsnull, 0, PR_TRUE, &color);
00261         if (NS_FAILED(rv))
00262             return NS_ERROR_DOM_SYNTAX_ERR;
00263 
00264         cairo_pattern_add_color_stop_rgba (mPattern, (double) offset,
00265                                            NS_GET_R(color) / 255.0,
00266                                            NS_GET_G(color) / 255.0,
00267                                            NS_GET_B(color) / 255.0,
00268                                            NS_GET_A(color) / 255.0);
00269         return NS_OK;
00270     }
00271 
00272     NS_DECL_ISUPPORTS
00273 
00274 protected:
00275     cairo_pattern_t *mPattern;
00276     nsCOMPtr<nsICSSParser> mCSSParser;
00277 };
00278 
00279 #ifndef MOZILLA_1_8_BRANCH
00280 NS_DEFINE_STATIC_IID_ACCESSOR(nsCanvasGradient, NS_CANVASGRADIENT_PRIVATE_IID)
00281 #endif
00282 
00283 NS_IMPL_ADDREF(nsCanvasGradient)
00284 NS_IMPL_RELEASE(nsCanvasGradient)
00285 
00286 NS_INTERFACE_MAP_BEGIN(nsCanvasGradient)
00287   NS_INTERFACE_MAP_ENTRY(nsCanvasGradient)
00288   NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasGradient)
00289   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(CanvasGradient)
00290   NS_INTERFACE_MAP_ENTRY(nsISupports)
00291 NS_INTERFACE_MAP_END
00292 
00296 #define NS_CANVASPATTERN_PRIVATE_IID \
00297     { 0xb85c6c8a, 0x0624, 0x4530, { 0xb8, 0xee, 0xff, 0xdf, 0x42, 0xe8, 0x21, 0x6d } }
00298 class nsCanvasPattern : public nsIDOMCanvasPattern
00299 {
00300 public:
00301 #ifdef MOZILLA_1_8_BRANCH
00302     NS_DEFINE_STATIC_IID_ACCESSOR(NS_CANVASPATTERN_PRIVATE_IID)
00303 #else
00304     NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASPATTERN_PRIVATE_IID)
00305 #endif
00306 
00307     nsCanvasPattern(cairo_pattern_t *cpat, PRUint8 *dataToFree,
00308                     nsIURI* URIForSecurityCheck, PRBool forceWriteOnly)
00309         : mPattern(cpat), mData(dataToFree), mURI(URIForSecurityCheck), mForceWriteOnly(forceWriteOnly)
00310     { }
00311 
00312     ~nsCanvasPattern() {
00313         if (mPattern)
00314             cairo_pattern_destroy(mPattern);
00315         if (mData)
00316             nsMemory::Free(mData);
00317     }
00318 
00319     void Apply(cairo_t *cairo) {
00320         cairo_set_source(cairo, mPattern);
00321     }
00322     
00323     nsIURI* GetURI() { return mURI; }
00324     PRBool GetForceWriteOnly() { return mForceWriteOnly; }
00325 
00326     NS_DECL_ISUPPORTS
00327 
00328 protected:
00329     cairo_pattern_t *mPattern;
00330     PRUint8 *mData;
00331     nsCOMPtr<nsIURI> mURI;
00332     PRPackedBool mForceWriteOnly;
00333 };
00334 
00335 #ifndef MOZILLA_1_8_BRANCH
00336 NS_DEFINE_STATIC_IID_ACCESSOR(nsCanvasPattern, NS_CANVASPATTERN_PRIVATE_IID)
00337 #endif
00338 
00339 NS_IMPL_ADDREF(nsCanvasPattern)
00340 NS_IMPL_RELEASE(nsCanvasPattern)
00341 
00342 NS_INTERFACE_MAP_BEGIN(nsCanvasPattern)
00343   NS_INTERFACE_MAP_ENTRY(nsCanvasPattern)
00344   NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasPattern)
00345   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(CanvasPattern)
00346   NS_INTERFACE_MAP_ENTRY(nsISupports)
00347 NS_INTERFACE_MAP_END
00348 
00352 class nsCanvasRenderingContext2D :
00353     public nsIDOMCanvasRenderingContext2D,
00354     public nsICanvasRenderingContextInternal
00355 {
00356 public:
00357     nsCanvasRenderingContext2D();
00358     virtual ~nsCanvasRenderingContext2D();
00359 
00360     nsresult Redraw();
00361     void SetCairoColor(nscolor c);
00362 
00363     // nsICanvasRenderingContextInternal
00364     NS_IMETHOD SetCanvasElement(nsICanvasElement* aParentCanvas);
00365     NS_IMETHOD SetDimensions(PRInt32 width, PRInt32 height);
00366     NS_IMETHOD Render(nsIRenderingContext *rc);
00367     NS_IMETHOD RenderToSurface(cairo_surface_t *surf);
00368     NS_IMETHOD GetInputStream(const nsACString& aMimeType,
00369                               const nsAString& aEncoderOptions,
00370                               nsIInputStream **aStream);
00371 
00372     // nsISupports interface
00373     NS_DECL_ISUPPORTS
00374 
00375     // nsIDOMCanvasRenderingContext2D interface
00376     NS_DECL_NSIDOMCANVASRENDERINGCONTEXT2D
00377 
00378 protected:
00379     // destroy cairo/image stuff, in preparation for possibly recreating
00380     void Destroy();
00381 
00382     nsIFrame *GetCanvasLayoutFrame();
00383 
00384     // Some helpers.  Doesn't modify acolor on failure.
00385     enum {
00386         STYLE_STROKE = 0,
00387         STYLE_FILL,
00388         STYLE_SHADOW
00389         //STYLE_MAX
00390     };
00391 
00392     // VC6 sucks
00393 #define STYLE_MAX 3
00394 
00395     nsresult SetStyleFromVariant(nsIVariant* aStyle, PRInt32 aWhichStyle);
00396     void StyleColorToString(const nscolor& aColor, nsAString& aStr);
00397 
00398     void DirtyAllStyles();
00399     void ApplyStyle(PRInt32 aWhichStyle);
00400     
00401     // If aURI has a different origin than the current script, then
00402     // we make the canvas write-only so bad guys can't extract the pixel
00403     // data.  If forceWriteOnly is set, we force write only to be set
00404     // and ignore aURI.  (This is used for when the original data came
00405     // from a <canvas> that had write-only set.)
00406     void DoDrawImageSecurityCheck(nsIURI* aURI, PRBool forceWriteOnly);
00407 
00408     // Member vars
00409     PRInt32 mWidth, mHeight;
00410 
00411     // the canvas element informs us when it's going away,
00412     // so these are not nsCOMPtrs
00413     nsICanvasElement* mCanvasElement;
00414 
00415     // our CSS parser, for colors and whatnot
00416     nsCOMPtr<nsICSSParser> mCSSParser;
00417 
00418     // yay cairo
00419 #ifdef MOZ_CAIRO_GFX
00420     nsRefPtr<gfxContext> mThebesContext;
00421     nsRefPtr<gfxASurface> mThebesSurface;
00422 #endif
00423 
00424     PRUint32 mSaveCount;
00425     cairo_t *mCairo;
00426     cairo_surface_t *mSurface;
00427 
00428     // only non-null if mSurface is an image surface
00429     PRUint8 *mImageSurfaceData;
00430 
00431     // style handling
00432     PRInt32 mLastStyle;
00433     PRPackedBool mDirtyStyle[STYLE_MAX];
00434 
00435     // state stack handling
00436     class ContextState {
00437     public:
00438         ContextState() : globalAlpha(1.0) { }
00439 
00440         ContextState(const ContextState& other)
00441             : globalAlpha(other.globalAlpha)
00442         {
00443             for (int i = 0; i < STYLE_MAX; i++) {
00444                 colorStyles[i] = other.colorStyles[i];
00445                 gradientStyles[i] = other.gradientStyles[i];
00446                 patternStyles[i] = other.patternStyles[i];
00447             }
00448         }
00449 
00450         inline void SetColorStyle(int whichStyle, nscolor color) {
00451             colorStyles[whichStyle] = color;
00452             gradientStyles[whichStyle] = nsnull;
00453             patternStyles[whichStyle] = nsnull;
00454         }
00455 
00456         inline void SetPatternStyle(int whichStyle, nsCanvasPattern* pat) {
00457             gradientStyles[whichStyle] = nsnull;
00458             patternStyles[whichStyle] = pat;
00459         }
00460 
00461         inline void SetGradientStyle(int whichStyle, nsCanvasGradient* grad) {
00462             gradientStyles[whichStyle] = grad;
00463             patternStyles[whichStyle] = nsnull;
00464         }
00465 
00466         float globalAlpha;
00467         nscolor colorStyles[STYLE_MAX];
00468         nsCOMPtr<nsCanvasGradient> gradientStyles[STYLE_MAX];
00469         nsCOMPtr<nsCanvasPattern> patternStyles[STYLE_MAX];
00470     };
00471 
00472     nsTArray<ContextState> mStyleStack;
00473 
00474     inline ContextState& CurrentState() {
00475         return mStyleStack[mSaveCount];
00476     }
00477 
00478 #ifdef MOZ_WIDGET_GTK2
00479     Pixmap mSurfacePixmap;
00480 #endif
00481 
00482     // stolen from nsJSUtils
00483     static PRBool ConvertJSValToUint32(PRUint32* aProp, JSContext* aContext,
00484                                        jsval aValue);
00485     static PRBool ConvertJSValToXPCObject(nsISupports** aSupports, REFNSIID aIID,
00486                                           JSContext* aContext, jsval aValue);
00487     static PRBool ConvertJSValToDouble(double* aProp, JSContext* aContext,
00488                                        jsval aValue);
00489 
00490     // cairo helpers
00491     nsresult CairoSurfaceFromElement(nsIDOMElement *imgElt,
00492                                      cairo_surface_t **aCairoSurface,
00493                                      PRUint8 **imgDataOut,
00494                                      PRInt32 *widthOut, PRInt32 *heightOut,
00495                                      nsIURI **uriOut, PRBool *forceWriteOnlyOut);
00496 
00497     nsresult DrawNativeSurfaces(nsIDrawingSurface* aBlackSurface,
00498                                 nsIDrawingSurface* aWhiteSurface,
00499                                 const nsIntSize& aSurfaceSize,
00500                                 nsIRenderingContext* aBlackContext);
00501 };
00502 
00503 NS_IMPL_ADDREF(nsCanvasRenderingContext2D)
00504 NS_IMPL_RELEASE(nsCanvasRenderingContext2D)
00505 
00506 NS_INTERFACE_MAP_BEGIN(nsCanvasRenderingContext2D)
00507   NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasRenderingContext2D)
00508   NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
00509   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMCanvasRenderingContext2D)
00510   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(CanvasRenderingContext2D)
00511 NS_INTERFACE_MAP_END
00512 
00517 nsresult
00518 NS_NewCanvasRenderingContext2D(nsIDOMCanvasRenderingContext2D** aResult)
00519 {
00520     nsIDOMCanvasRenderingContext2D* ctx = new nsCanvasRenderingContext2D();
00521     if (!ctx)
00522         return NS_ERROR_OUT_OF_MEMORY;
00523 
00524     NS_ADDREF(*aResult = ctx);
00525     return NS_OK;
00526 }
00527 
00528 nsCanvasRenderingContext2D::nsCanvasRenderingContext2D()
00529     : mCanvasElement(nsnull),
00530       mSaveCount(0), mCairo(nsnull), mSurface(nsnull), mImageSurfaceData(nsnull), mStyleStack(20)
00531 {
00532 #ifdef MOZ_WIDGET_GTK2
00533     mSurfacePixmap = None;
00534 #endif
00535 }
00536 
00537 nsCanvasRenderingContext2D::~nsCanvasRenderingContext2D()
00538 {
00539     Destroy();
00540 }
00541 
00542 nsIFrame*
00543 nsCanvasRenderingContext2D::GetCanvasLayoutFrame()
00544 {
00545     if (!mCanvasElement)
00546         return nsnull;
00547 
00548     nsIFrame *fr = nsnull;
00549     mCanvasElement->GetPrimaryCanvasFrame(&fr);
00550     return fr;
00551 }
00552 
00553 void
00554 nsCanvasRenderingContext2D::Destroy()
00555 {
00556     if (mCairo) {
00557         cairo_destroy(mCairo);
00558         mCairo = nsnull;
00559     }
00560 
00561     if (mSurface) {
00562         cairo_surface_destroy(mSurface);
00563         mSurface = nsnull;
00564     }
00565 
00566 #ifdef MOZ_WIDGET_GTK2
00567     if (mSurfacePixmap != None) {
00568         XFreePixmap(GDK_DISPLAY(), mSurfacePixmap);
00569         mSurfacePixmap = None;
00570     }
00571 #endif
00572 
00573     if (mImageSurfaceData) {
00574         PR_Free (mImageSurfaceData);
00575         mImageSurfaceData = nsnull;
00576     }
00577 }
00578 
00579 nsresult
00580 nsCanvasRenderingContext2D::SetStyleFromVariant(nsIVariant* aStyle, PRInt32 aWhichStyle)
00581 {
00582     nsresult rv;
00583     nscolor color;
00584 
00585     PRUint16 paramType;
00586     rv = aStyle->GetDataType(&paramType);
00587     NS_ENSURE_SUCCESS(rv, rv);
00588 
00589     if (paramType == nsIDataType::VTYPE_DOMSTRING) {
00590         nsString str;
00591         rv = aStyle->GetAsDOMString(str);
00592         NS_ENSURE_SUCCESS(rv, rv);
00593 
00594         rv = mCSSParser->ParseColorString(str, nsnull, 0, PR_TRUE, &color);
00595         if (NS_FAILED(rv))
00596             return NS_ERROR_DOM_SYNTAX_ERR;
00597 
00598         CurrentState().SetColorStyle(aWhichStyle, color);
00599 
00600         mDirtyStyle[aWhichStyle] = PR_TRUE;
00601         return NS_OK;
00602     } else if (paramType == nsIDataType::VTYPE_WSTRING_SIZE_IS) {
00603         nsAutoString str;
00604 
00605         rv = aStyle->GetAsAString(str);
00606         NS_ENSURE_SUCCESS(rv, rv);
00607 
00608         rv = mCSSParser->ParseColorString(str, nsnull, 0, PR_TRUE, &color);
00609         if (NS_FAILED(rv))
00610             return NS_ERROR_DOM_SYNTAX_ERR;
00611 
00612         CurrentState().SetColorStyle(aWhichStyle, color);
00613 
00614         mDirtyStyle[aWhichStyle] = PR_TRUE;
00615         return NS_OK;
00616     } else if (paramType == nsIDataType::VTYPE_INTERFACE ||
00617                paramType == nsIDataType::VTYPE_INTERFACE_IS)
00618     {
00619         nsID *iid;
00620         nsCOMPtr<nsISupports> iface;
00621         rv = aStyle->GetAsInterface(&iid, getter_AddRefs(iface));
00622 
00623         nsCOMPtr<nsCanvasGradient> grad(do_QueryInterface(iface));
00624         if (grad) {
00625             CurrentState().SetGradientStyle(aWhichStyle, grad);
00626             mDirtyStyle[aWhichStyle] = PR_TRUE;
00627             return NS_OK;
00628         }
00629 
00630         nsCOMPtr<nsCanvasPattern> pattern(do_QueryInterface(iface));
00631         if (pattern) {
00632             CurrentState().SetPatternStyle(aWhichStyle, pattern);
00633             mDirtyStyle[aWhichStyle] = PR_TRUE;
00634             return NS_OK;
00635         }
00636     }
00637 
00638     return NS_ERROR_DOM_SYNTAX_ERR;
00639 }
00640 
00641 void
00642 nsCanvasRenderingContext2D::StyleColorToString(const nscolor& aColor, nsAString& aStr)
00643 {
00644     if (NS_GET_A(aColor) == 255) {
00645         CopyUTF8toUTF16(nsPrintfCString(100, "#%02x%02x%02x",
00646                                         NS_GET_R(aColor),
00647                                         NS_GET_G(aColor),
00648                                         NS_GET_B(aColor)),
00649                         aStr);
00650     } else {
00651         CopyUTF8toUTF16(nsPrintfCString(100, "rgba(%d,%d,%d,%0.2f)",
00652                                         NS_GET_R(aColor),
00653                                         NS_GET_G(aColor),
00654                                         NS_GET_B(aColor),
00655                                         NS_GET_A(aColor) / 255.0f),
00656                         aStr);
00657     }
00658 }
00659 
00660 void
00661 nsCanvasRenderingContext2D::DirtyAllStyles()
00662 {
00663     for (int i = 0; i < STYLE_MAX; i++) {
00664         mDirtyStyle[i] = PR_TRUE;
00665     }
00666 }
00667 
00668 void
00669 nsCanvasRenderingContext2D::DoDrawImageSecurityCheck(nsIURI* aURI, PRBool forceWriteOnly)
00670 {
00671     // Callers should ensure that mCanvasElement is non-null before calling this
00672     if (!mCanvasElement) {
00673         NS_WARNING("DoDrawImageSecurityCheck called without canvas element!");
00674         return;
00675     }
00676 
00677     if (mCanvasElement->IsWriteOnly())
00678         return;
00679 
00680     // If we explicitly set WriteOnly just do it and get out
00681     if (forceWriteOnly) {
00682         mCanvasElement->SetWriteOnly();
00683         return;
00684     }
00685 
00686     // Further security checks require a URI. If we don't have one the image
00687     // must be another canvas and we inherit the forceWriteOnly state
00688     if (!aURI)
00689         return;
00690 
00691     nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
00692 
00693 #ifdef MOZILLA_1_8_BRANCH
00694     nsCOMPtr<nsIDOMNode> elem = do_QueryInterface(mCanvasElement);
00695     if (elem && ssm) {
00696         nsCOMPtr<nsIPrincipal> elemPrincipal;
00697         nsCOMPtr<nsIPrincipal> uriPrincipal;
00698         nsCOMPtr<nsIDocument> elemDocument;
00699         nsContentUtils::GetDocumentAndPrincipal(elem, getter_AddRefs(elemDocument), getter_AddRefs(elemPrincipal));
00700         ssm->GetCodebasePrincipal(aURI, getter_AddRefs(uriPrincipal));
00701 
00702         if (uriPrincipal && elemPrincipal) {
00703             nsresult rv =
00704                 ssm->CheckSameOriginPrincipal(elemPrincipal, uriPrincipal);
00705             if (NS_SUCCEEDED(rv)) {
00706                 // Same origin
00707                 return;
00708             }
00709         }
00710     }
00711 #else
00712     nsCOMPtr<nsINode> elem = do_QueryInterface(mCanvasElement);
00713     if (elem && ssm) {
00714         nsCOMPtr<nsIPrincipal> uriPrincipal;
00715         ssm->GetCodebasePrincipal(aURI, getter_AddRefs(uriPrincipal));
00716 
00717         if (uriPrincipal) {
00718             nsresult rv = ssm->CheckSameOriginPrincipal(elem->NodePrincipal(),
00719                                                         uriPrincipal);
00720             if (NS_SUCCEEDED(rv)) {
00721                 // Same origin
00722                 return;
00723             }
00724         }
00725     }
00726 #endif
00727 
00728     mCanvasElement->SetWriteOnly();
00729 }
00730 
00731 void
00732 nsCanvasRenderingContext2D::ApplyStyle(PRInt32 aWhichStyle)
00733 {
00734     if (mLastStyle == aWhichStyle &&
00735         !mDirtyStyle[aWhichStyle])
00736     {
00737         // nothing to do, this is already the set style
00738         return;
00739     }
00740 
00741     mDirtyStyle[aWhichStyle] = PR_FALSE;
00742     mLastStyle = aWhichStyle;
00743 
00744     nsCanvasPattern* pattern = CurrentState().patternStyles[aWhichStyle];
00745     if (pattern) {
00746         if (!mCanvasElement)
00747             return;
00748 
00749         DoDrawImageSecurityCheck(pattern->GetURI(), pattern->GetForceWriteOnly());
00750         pattern->Apply(mCairo);
00751         return;
00752     }
00753 
00754     if (CurrentState().gradientStyles[aWhichStyle]) {
00755         CurrentState().gradientStyles[aWhichStyle]->Apply(mCairo);
00756         return;
00757     }
00758 
00759     SetCairoColor(CurrentState().colorStyles[aWhichStyle]);
00760 }
00761 
00762 nsresult
00763 nsCanvasRenderingContext2D::Redraw()
00764 {
00765     nsIFrame *frame = GetCanvasLayoutFrame();
00766     if (frame) {
00767         nsRect r = frame->GetRect();
00768         r.x = r.y = 0;
00769         frame->Invalidate(r, PR_FALSE);
00770     }
00771 
00772     return NS_OK;
00773 }
00774 
00775 void
00776 nsCanvasRenderingContext2D::SetCairoColor(nscolor c)
00777 {
00778     double r = double(NS_GET_R(c) / 255.0);
00779     double g = double(NS_GET_G(c) / 255.0);
00780     double b = double(NS_GET_B(c) / 255.0);
00781     double a = double(NS_GET_A(c) / 255.0) * CurrentState().globalAlpha;
00782 
00783     cairo_set_source_rgba (mCairo, r, g, b, a);
00784 }
00785 
00786 NS_IMETHODIMP
00787 nsCanvasRenderingContext2D::SetDimensions(PRInt32 width, PRInt32 height)
00788 {
00789     Destroy();
00790 
00791     // Check that the dimensions are sane
00792     if (!CheckSaneImageSize(width, height))
00793         return NS_ERROR_FAILURE;
00794 
00795     mWidth = width;
00796     mHeight = height;
00797 
00798 #ifdef MOZ_CAIRO_GFX
00799     mThebesSurface = gfxPlatform::GetPlatform()->CreateOffscreenSurface(width, height, gfxASurface::ImageFormatARGB32);
00800     mThebesContext = new gfxContext(mThebesSurface);
00801 
00802     mSurface = mThebesSurface->CairoSurface();
00803     cairo_surface_reference(mSurface);
00804     mCairo = mThebesContext->GetCairo();
00805     cairo_reference(mCairo);
00806 #else
00807     // non-cairo gfx
00808 #ifdef XP_WIN
00809 #ifndef MOZILLA_1_8_BRANCH
00810     mSurface = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32,
00811                                                     mWidth, mHeight);
00812 #else
00813     mSurface = _cairo_win32_surface_create_dib (CAIRO_FORMAT_ARGB32,
00814                                                 mWidth, mHeight);
00815 #endif
00816 #elif MOZ_WIDGET_GTK2
00817     // On most current X servers, using the software-only surface
00818     // actually provides a much smoother and faster display.
00819     // However, we provide MOZ_CANVAS_USE_RENDER for whomever wants to
00820     // go that route.
00821     if (getenv("MOZ_CANVAS_USE_RENDER")) {
00822         XRenderPictFormat *fmt = XRenderFindStandardFormat (GDK_DISPLAY(),
00823                                                             PictStandardARGB32);
00824         if (fmt) {
00825             int npfmts = 0;
00826             XPixmapFormatValues *pfmts = XListPixmapFormats(GDK_DISPLAY(), &npfmts);
00827             for (int i = 0; i < npfmts; i++) {
00828                 if (pfmts[i].depth == 32) {
00829                     npfmts = -1;
00830                     break;
00831                 }
00832             }
00833             XFree(pfmts);
00834 
00835             if (npfmts == -1) {
00836                 mSurfacePixmap = XCreatePixmap (GDK_DISPLAY(),
00837                                                 DefaultRootWindow(GDK_DISPLAY()),
00838                                                 width, height, 32);
00839                 mSurface = cairo_xlib_surface_create_with_xrender_format
00840                     (GDK_DISPLAY(), mSurfacePixmap, DefaultScreenOfDisplay(GDK_DISPLAY()),
00841                      fmt, mWidth, mHeight);
00842             }
00843         }
00844     }
00845 #endif
00846 
00847     // fall back to image surface
00848     if (!mSurface) {
00849         mImageSurfaceData = (PRUint8*) PR_Malloc (mWidth * mHeight * 4);
00850         if (!mImageSurfaceData)
00851             return NS_ERROR_OUT_OF_MEMORY;
00852 
00853         mSurface = cairo_image_surface_create_for_data (mImageSurfaceData,
00854                                                         CAIRO_FORMAT_ARGB32,
00855                                                         mWidth, mHeight,
00856                                                         mWidth * 4);
00857     }
00858 
00859     mCairo = cairo_create(mSurface);
00860 #endif
00861 
00862     // set up the initial canvas defaults
00863     mStyleStack.Clear();
00864     mSaveCount = 0;
00865 
00866     ContextState *state = mStyleStack.AppendElement();
00867     state->globalAlpha = 1.0;
00868     for (int i = 0; i < STYLE_MAX; i++)
00869         state->colorStyles[i] = NS_RGB(0,0,0);
00870     mLastStyle = -1;
00871 
00872     DirtyAllStyles();
00873 
00874     cairo_set_operator(mCairo, CAIRO_OPERATOR_CLEAR);
00875     cairo_new_path(mCairo);
00876     cairo_rectangle(mCairo, 0, 0, mWidth, mHeight);
00877     cairo_fill(mCairo);
00878 
00879     cairo_set_line_width(mCairo, 1.0);
00880     cairo_set_operator(mCairo, CAIRO_OPERATOR_OVER);
00881     cairo_set_miter_limit(mCairo, 10.0);
00882     cairo_set_line_cap(mCairo, CAIRO_LINE_CAP_BUTT);
00883     cairo_set_line_join(mCairo, CAIRO_LINE_JOIN_MITER);
00884 
00885     cairo_new_path(mCairo);
00886 
00887     return NS_OK;
00888 }
00889  
00890 NS_IMETHODIMP
00891 nsCanvasRenderingContext2D::Render(nsIRenderingContext *rc)
00892 {
00893     nsresult rv = NS_OK;
00894 
00895     if (!mSurface || !mCairo ||
00896         cairo_surface_status(mSurface) ||
00897         cairo_status(mCairo))
00898         return NS_ERROR_FAILURE;
00899 
00900 #ifdef MOZ_CAIRO_GFX
00901 
00902     if (!mThebesSurface)
00903         return NS_ERROR_FAILURE;
00904 
00905     gfxContext* ctx = (gfxContext*) rc->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT);
00906     nsRefPtr<gfxPattern> pat = new gfxPattern(mThebesSurface);
00907 
00908     // XXX I don't want to use PixelSnapped here, but layout doesn't guarantee
00909     // pixel alignment for this stuff!
00910     ctx->NewPath();
00911     ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, mWidth, mHeight), pat);
00912     ctx->Fill();
00913 
00914 #else
00915 
00916     // non-Thebes; this becomes exciting
00917     cairo_surface_t *dest = nsnull;
00918     cairo_t *dest_cr = nsnull;
00919 
00920 #ifdef XP_WIN
00921     void *ptr = nsnull;
00922 #ifdef MOZILLA_1_8_BRANCH
00923     rv = rc->RetrieveCurrentNativeGraphicData(&ptr);
00924     if (NS_FAILED(rv) || !ptr)
00925         return NS_ERROR_FAILURE;
00926 #else
00927     ptr = rc->GetNativeGraphicData(nsIRenderingContext::NATIVE_WINDOWS_DC);
00928 #endif
00929     HDC dc = (HDC) ptr;
00930 
00931     dest = cairo_win32_surface_create (dc);
00932     dest_cr = cairo_create (dest);
00933 #endif
00934 
00935 #ifdef XP_OS2
00936     void *ptr = nsnull;
00937 #ifdef MOZILLA_1_8_BRANCH
00938     rv = rc->RetrieveCurrentNativeGraphicData(&ptr);
00939     if (NS_FAILED(rv) || !ptr)
00940         return NS_ERROR_FAILURE;
00941 #else
00942     /* OS/2 also uses NATIVE_WINDOWS_DC to get a native OS/2 PS */
00943     ptr = rc->GetNativeGraphicData(nsIRenderingContext::NATIVE_WINDOWS_DC);
00944 #endif
00945 
00946     HPS hps = (HPS)ptr;
00947     nsDrawingSurfaceOS2 *surface; /* to get the dimensions from this */
00948     PRUint32 width, height;
00949     rc->GetDrawingSurface((nsIDrawingSurface**)&surface);
00950     surface->GetDimensions(&width, &height);
00951 
00952     dest = cairo_os2_surface_create(hps, width, height);
00953     cairo_surface_mark_dirty(dest); // needed on OS/2 for initialization
00954     dest_cr = cairo_create(dest);
00955 #endif
00956 
00957 #ifdef MOZ_WIDGET_GTK2
00958     GdkDrawable *gdkdraw = nsnull;
00959 #ifdef MOZILLA_1_8_BRANCH
00960     rv = rc->RetrieveCurrentNativeGraphicData((void**) &gdkdraw);
00961     if (NS_FAILED(rv) || !gdkdraw)
00962         return NS_ERROR_FAILURE;
00963 #else
00964     gdkdraw = (GdkDrawable*) rc->GetNativeGraphicData(nsIRenderingContext::NATIVE_GDK_DRAWABLE);
00965     if (!gdkdraw)
00966         return NS_ERROR_FAILURE;
00967 #endif
00968 
00969     gint w, h;
00970     gdk_drawable_get_size (gdkdraw, &w, &h);
00971     dest = cairo_xlib_surface_create (GDK_DRAWABLE_XDISPLAY(gdkdraw),
00972                                       GDK_DRAWABLE_XID(gdkdraw),
00973                                       GDK_VISUAL_XVISUAL(gdk_drawable_get_visual(gdkdraw)),
00974                                       w, h);
00975     dest_cr = cairo_create (dest);
00976 #endif
00977 
00978     nsTransform2D *tx = nsnull;
00979     rc->GetCurrentTransform(tx);
00980 
00981     nsIDeviceContext *dctx;
00982     rc->GetDeviceContext(dctx);
00983 
00984     // Until we can use the quartz2 surface, mac will be different,
00985     // since we'll use CG to render.
00986 #ifndef XP_MACOSX
00987 
00988     float x0 = 0.0, y0 = 0.0;
00989     float sx = 1.0, sy = 1.0;
00990 
00991     if (tx->GetType() & MG_2DTRANSLATION) {
00992         tx->Transform(&x0, &y0);
00993     }
00994 
00995     if (tx->GetType() & MG_2DSCALE) {
00996         sx = sy = dctx->DevUnitsToTwips();
00997         tx->TransformNoXLate(&sx, &sy);
00998     }
00999 
01000     cairo_translate (dest_cr, NSToIntRound(x0), NSToIntRound(y0));
01001     if (sx != 1.0 || sy != 1.0)
01002         cairo_scale (dest_cr, sx, sy);
01003 
01004     cairo_rectangle (dest_cr, 0, 0, mWidth, mHeight);
01005     cairo_clip (dest_cr);
01006 
01007     cairo_set_source_surface (dest_cr, mSurface, 0, 0);
01008     cairo_paint (dest_cr);
01009 
01010     if (dest_cr)
01011         cairo_destroy (dest_cr);
01012     if (dest)
01013         cairo_surface_destroy (dest);
01014 
01015 #else
01016 
01017     // OSX path
01018     nsIDrawingSurface *ds = nsnull;
01019     rc->GetDrawingSurface(&ds);
01020     if (!ds)
01021         return NS_ERROR_FAILURE;
01022 
01023     nsDrawingSurfaceMac *macds = NS_STATIC_CAST(nsDrawingSurfaceMac*, ds);
01024     CGContextRef cgc = macds->StartQuartzDrawing();
01025 
01026     CGDataProviderRef dataProvider;
01027     CGImageRef img;
01028 
01029     dataProvider = CGDataProviderCreateWithData (NULL, mImageSurfaceData,
01030                                                  mWidth * mHeight * 4,
01031                                                  NULL);
01032     CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
01033     img = CGImageCreate (mWidth, mHeight, 8, 32, mWidth * 4, rgb,
01034                          (CGImageAlphaInfo)(kCGImageAlphaPremultipliedFirst |
01035                                             CG_BITMAP_BYTE_ORDER_FLAG),
01036                          dataProvider, NULL, false, kCGRenderingIntentDefault);
01037     CGColorSpaceRelease (rgb);
01038     CGDataProviderRelease (dataProvider);
01039 
01040     float x0 = 0.0, y0 = 0.0;
01041     float sx = 1.0, sy = 1.0;
01042     if (tx->GetType() & MG_2DTRANSLATION) {
01043         tx->Transform(&x0, &y0);
01044     }
01045 
01046     if (tx->GetType() & MG_2DSCALE) {
01047         float p2t = dctx->DevUnitsToTwips();
01048         sx = p2t, sy = p2t;
01049         tx->TransformNoXLate(&sx, &sy);
01050     }
01051 
01052     CGContextTranslateCTM (cgc, NSToIntRound(x0), NSToIntRound(y0));
01053     if (sx != 1.0 || sy != 1.0)
01054         CGContextScaleCTM (cgc, sx, sy);
01055 
01056     // flip, so that the image gets drawn correct-side up
01057     CGContextScaleCTM (cgc, 1.0, -1.0);
01058     CGContextDrawImage (cgc, CGRectMake(0, -mHeight, mWidth, mHeight), img);
01059 
01060     CGImageRelease (img);
01061 
01062     macds->EndQuartzDrawing(cgc);
01063 
01064     rv = NS_OK;
01065 #endif
01066 #endif
01067 
01068     return rv;
01069 }
01070 
01071 NS_IMETHODIMP
01072 nsCanvasRenderingContext2D::RenderToSurface(cairo_surface_t *surf)
01073 {
01074     cairo_t *cr = cairo_create (surf);
01075     cairo_set_source_surface (cr, mSurface, 0, 0);
01076     cairo_paint (cr);
01077     cairo_destroy (cr);
01078 
01079     return NS_OK;
01080 }
01081  
01082 NS_IMETHODIMP
01083 nsCanvasRenderingContext2D::GetInputStream(const nsACString& aMimeType,
01084                                            const nsAString& aEncoderOptions,
01085                                            nsIInputStream **aStream)
01086 {
01087     if (!mSurface || cairo_surface_status(mSurface) != CAIRO_STATUS_SUCCESS)
01088         return NS_ERROR_FAILURE;
01089 
01090     nsCString conid(NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type="));
01091     conid += aMimeType;
01092 
01093     nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid.get());
01094     if (!encoder)
01095         return NS_ERROR_FAILURE;
01096 
01097     if (mImageSurfaceData) {
01098         encoder->InitFromData(mImageSurfaceData,
01099                               mWidth * mHeight * 4, mWidth, mHeight, mWidth * 4,
01100                               imgIEncoder::INPUT_FORMAT_HOSTARGB,
01101                               aEncoderOptions);
01102     } else {
01103         nsAutoArrayPtr<PRUint8> imageBuffer((PRUint8*) PR_Malloc(sizeof(PRUint8) * mWidth * mHeight * 4));
01104         if (!imageBuffer)
01105             return NS_ERROR_FAILURE;
01106 
01107         cairo_surface_t *imgsurf = cairo_image_surface_create_for_data (imageBuffer.get(),
01108                                                                         CAIRO_FORMAT_ARGB32,
01109                                                                         mWidth, mHeight, mWidth * 4);
01110         if (!imgsurf || cairo_surface_status(imgsurf))
01111             return NS_ERROR_FAILURE;
01112 
01113         cairo_t *cr = cairo_create(imgsurf);
01114         cairo_surface_destroy (imgsurf);
01115 
01116         cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
01117         cairo_set_source_surface (cr, mSurface, 0, 0);
01118         cairo_paint (cr);
01119         cairo_destroy (cr);
01120 
01121         encoder->InitFromData(imageBuffer.get(),
01122                               mWidth * mHeight * 4, mWidth, mHeight, mWidth * 4,
01123                               imgIEncoder::INPUT_FORMAT_HOSTARGB,
01124                               aEncoderOptions);
01125     }
01126 
01127     return CallQueryInterface(encoder, aStream);
01128 }
01129 
01130 //
01131 // nsCanvasRenderingContext2D impl
01132 //
01133 
01134 NS_IMETHODIMP
01135 nsCanvasRenderingContext2D::SetCanvasElement(nsICanvasElement* aCanvasElement)
01136 {
01137     // don't hold a ref to this!
01138     mCanvasElement = aCanvasElement;
01139 
01140     // set up our css parser, if necessary
01141     if (!mCSSParser) {
01142         mCSSParser = do_CreateInstance("@mozilla.org/content/css-parser;1");
01143     }
01144 
01145     return NS_OK;
01146 }
01147 
01148 NS_IMETHODIMP
01149 nsCanvasRenderingContext2D::GetCanvas(nsIDOMHTMLCanvasElement **canvas)
01150 {
01151     if (mCanvasElement == nsnull) {
01152         *canvas = nsnull;
01153         return NS_OK;
01154     }
01155 
01156     return CallQueryInterface(mCanvasElement, canvas);
01157 }
01158 
01159 //
01160 // state
01161 //
01162 
01163 NS_IMETHODIMP
01164 nsCanvasRenderingContext2D::Save()
01165 {
01166     ContextState state = CurrentState();
01167     mStyleStack.AppendElement(state);
01168     cairo_save (mCairo);
01169     mSaveCount++;
01170     return NS_OK;
01171 }
01172 
01173 NS_IMETHODIMP
01174 nsCanvasRenderingContext2D::Restore()
01175 {
01176     if (mSaveCount <= 0)
01177         return NS_ERROR_DOM_INVALID_STATE_ERR;
01178 
01179     mStyleStack.RemoveElementAt(mSaveCount);
01180     cairo_restore (mCairo);
01181 
01182     mLastStyle = -1;
01183     DirtyAllStyles();
01184 
01185     mSaveCount--;
01186     return NS_OK;
01187 }
01188 
01189 //
01190 // transformations
01191 //
01192 
01193 NS_IMETHODIMP
01194 nsCanvasRenderingContext2D::Scale(float x, float y)
01195 {
01196     if (!FloatValidate(x,y))
01197         return NS_ERROR_DOM_SYNTAX_ERR;
01198 
01199     cairo_scale (mCairo, x, y);
01200     return NS_OK;
01201 }
01202 
01203 NS_IMETHODIMP
01204 nsCanvasRenderingContext2D::Rotate(float angle)
01205 {
01206     if (!FloatValidate(angle))
01207         return NS_ERROR_DOM_SYNTAX_ERR;
01208 
01209     cairo_rotate (mCairo, angle);
01210     return NS_OK;
01211 }
01212 
01213 NS_IMETHODIMP
01214 nsCanvasRenderingContext2D::Translate(float x, float y)
01215 {
01216     if (!FloatValidate(x,y))
01217         return NS_ERROR_DOM_SYNTAX_ERR;
01218 
01219     cairo_translate (mCairo, x, y);
01220     return NS_OK;
01221 }
01222 
01223 //
01224 // colors
01225 //
01226 
01227 NS_IMETHODIMP
01228 nsCanvasRenderingContext2D::SetGlobalAlpha(float aGlobalAlpha)
01229 {
01230     if (!FloatValidate(aGlobalAlpha))
01231         return NS_ERROR_DOM_SYNTAX_ERR;
01232 
01233     // ignore invalid values, as per spec
01234     if (aGlobalAlpha < 0.0 || aGlobalAlpha > 1.0)
01235         return NS_OK;
01236 
01237     CurrentState().globalAlpha = aGlobalAlpha;
01238     return NS_OK;
01239 }
01240 
01241 NS_IMETHODIMP
01242 nsCanvasRenderingContext2D::GetGlobalAlpha(float *aGlobalAlpha)
01243 {
01244     *aGlobalAlpha = CurrentState().globalAlpha;
01245     return NS_OK;
01246 }
01247 
01248 NS_IMETHODIMP
01249 nsCanvasRenderingContext2D::SetStrokeStyle(nsIVariant* aStyle)
01250 {
01251     return SetStyleFromVariant(aStyle, STYLE_STROKE);
01252 }
01253 
01254 NS_IMETHODIMP
01255 nsCanvasRenderingContext2D::GetStrokeStyle(nsIVariant** aStyle)
01256 {
01257     nsresult rv;
01258 
01259     nsCOMPtr<nsIWritableVariant> var = do_CreateInstance("@mozilla.org/variant;1");
01260     if (!var)
01261         return NS_ERROR_FAILURE;
01262     rv = var->SetWritable(PR_TRUE);
01263     NS_ENSURE_SUCCESS(rv, rv);
01264 
01265     if (CurrentState().patternStyles[STYLE_STROKE]) {
01266         rv = var->SetAsISupports(CurrentState().patternStyles[STYLE_STROKE]);
01267         NS_ENSURE_SUCCESS(rv, rv);
01268     } else if (CurrentState().gradientStyles[STYLE_STROKE]) {
01269         rv = var->SetAsISupports(CurrentState().gradientStyles[STYLE_STROKE]);
01270         NS_ENSURE_SUCCESS(rv, rv);
01271     } else {
01272         nsString styleStr;
01273         StyleColorToString(CurrentState().colorStyles[STYLE_STROKE], styleStr);
01274 
01275         rv = var->SetAsDOMString(styleStr);
01276         NS_ENSURE_SUCCESS(rv, rv);
01277     }
01278 
01279     NS_ADDREF(*aStyle = var);
01280     return NS_OK;
01281 }
01282 
01283 NS_IMETHODIMP
01284 nsCanvasRenderingContext2D::SetFillStyle(nsIVariant* aStyle)
01285 {
01286     return SetStyleFromVariant(aStyle, STYLE_FILL);
01287 }
01288 
01289 NS_IMETHODIMP
01290 nsCanvasRenderingContext2D::GetFillStyle(nsIVariant** aStyle)
01291 {
01292     nsresult rv;
01293 
01294     nsCOMPtr<nsIWritableVariant> var = do_CreateInstance("@mozilla.org/variant;1");
01295     if (!var)
01296         return NS_ERROR_FAILURE;
01297     rv = var->SetWritable(PR_TRUE);
01298     NS_ENSURE_SUCCESS(rv, rv);
01299 
01300     if (CurrentState().patternStyles[STYLE_FILL]) {
01301         rv = var->SetAsISupports(CurrentState().patternStyles[STYLE_FILL]);
01302         NS_ENSURE_SUCCESS(rv, rv);
01303     } else if (CurrentState().gradientStyles[STYLE_FILL]) {
01304         rv = var->SetAsISupports(CurrentState().gradientStyles[STYLE_FILL]);
01305         NS_ENSURE_SUCCESS(rv, rv);
01306     } else {
01307         nsString styleStr;
01308         StyleColorToString(CurrentState().colorStyles[STYLE_FILL], styleStr);
01309 
01310         rv = var->SetAsDOMString(styleStr);
01311         NS_ENSURE_SUCCESS(rv, rv);
01312     }
01313 
01314     NS_ADDREF(*aStyle = var);
01315     return NS_OK;
01316 }
01317 
01318 //
01319 // gradients and patterns
01320 //
01321 NS_IMETHODIMP
01322 nsCanvasRenderingContext2D::CreateLinearGradient(float x0, float y0, float x1, float y1,
01323                                                  nsIDOMCanvasGradient **_retval)
01324 {
01325     if (!FloatValidate(x0,y0,x1,y1))
01326         return NS_ERROR_DOM_SYNTAX_ERR;
01327 
01328     cairo_pattern_t *gradpat = nsnull;
01329     gradpat = cairo_pattern_create_linear ((double) x0, (double) y0, (double) x1, (double) y1);
01330     nsCanvasGradient *grad = new nsCanvasGradient(gradpat, mCSSParser);
01331     if (!grad) {
01332         cairo_pattern_destroy(gradpat);
01333         return NS_ERROR_OUT_OF_MEMORY;
01334     }
01335 
01336     NS_ADDREF(*_retval = grad);
01337     return NS_OK;
01338 }
01339 
01340 NS_IMETHODIMP
01341 nsCanvasRenderingContext2D::CreateRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1,
01342                                                  nsIDOMCanvasGradient **_retval)
01343 {
01344     if (!FloatValidate(x0,y0,r0,x1,y1,r1))
01345         return NS_ERROR_DOM_SYNTAX_ERR;
01346 
01347     cairo_pattern_t *gradpat = nsnull;
01348     gradpat = cairo_pattern_create_radial ((double) x0, (double) y0, (double) r0,
01349                                            (double) x1, (double) y1, (double) r1);
01350     nsCanvasGradient *grad = new nsCanvasGradient(gradpat, mCSSParser);
01351     if (!grad) {
01352         cairo_pattern_destroy(gradpat);
01353         return NS_ERROR_OUT_OF_MEMORY;
01354     }
01355 
01356     NS_ADDREF(*_retval = grad);
01357     return NS_OK;
01358 }
01359 
01360 NS_IMETHODIMP
01361 nsCanvasRenderingContext2D::CreatePattern(nsIDOMHTMLElement *image,
01362                                           const nsAString& repeat,
01363                                           nsIDOMCanvasPattern **_retval)
01364 {
01365     nsresult rv;
01366     cairo_extend_t extend;
01367 
01368     if (repeat.IsEmpty() || repeat.EqualsLiteral("repeat")) {
01369         extend = CAIRO_EXTEND_REPEAT;
01370     } else if (repeat.EqualsLiteral("repeat-x")) {
01371         // XX
01372         extend = CAIRO_EXTEND_REPEAT;
01373     } else if (repeat.EqualsLiteral("repeat-y")) {
01374         // XX
01375         extend = CAIRO_EXTEND_REPEAT;
01376     } else if (repeat.EqualsLiteral("no-repeat")) {
01377         extend = CAIRO_EXTEND_NONE;
01378     } else {
01379         // XXX ERRMSG we need to report an error to developers here! (bug 329026)
01380         return NS_ERROR_DOM_SYNTAX_ERR;
01381     }
01382 
01383     cairo_surface_t *imgSurf = nsnull;
01384     PRUint8 *imgData = nsnull;
01385     PRInt32 imgWidth, imgHeight;
01386     nsCOMPtr<nsIURI> uri;
01387     PRBool forceWriteOnly = PR_FALSE;
01388     rv = CairoSurfaceFromElement(image, &imgSurf, &imgData,
01389                                  &imgWidth, &imgHeight, getter_AddRefs(uri), &forceWriteOnly);
01390     if (NS_FAILED(rv))
01391         return rv;
01392 
01393     cairo_pattern_t *cairopat = cairo_pattern_create_for_surface(imgSurf);
01394     cairo_surface_destroy(imgSurf);
01395 
01396     cairo_pattern_set_extend (cairopat, extend);
01397 
01398     nsCanvasPattern *pat = new nsCanvasPattern(cairopat, imgData, uri, forceWriteOnly);
01399     if (!pat) {
01400         cairo_pattern_destroy(cairopat);
01401         nsMemory::Free(imgData);
01402         return NS_ERROR_OUT_OF_MEMORY;
01403     }
01404 
01405     NS_ADDREF(*_retval = pat);
01406     return NS_OK;
01407 }
01408 
01409 //
01410 // shadows
01411 //
01412 NS_IMETHODIMP
01413 nsCanvasRenderingContext2D::SetShadowOffsetX(float x)
01414 {
01415     if (!FloatValidate(x))
01416         return NS_ERROR_DOM_SYNTAX_ERR;
01417     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
01418     return NS_OK;
01419 }
01420 
01421 NS_IMETHODIMP
01422 nsCanvasRenderingContext2D::GetShadowOffsetX(float *x)
01423 {
01424     *x = 0.0f;
01425     return NS_OK;
01426 }
01427 
01428 NS_IMETHODIMP
01429 nsCanvasRenderingContext2D::SetShadowOffsetY(float y)
01430 {
01431     if (!FloatValidate(y))
01432         return NS_ERROR_DOM_SYNTAX_ERR;
01433     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
01434     return NS_OK;
01435 }
01436 
01437 NS_IMETHODIMP
01438 nsCanvasRenderingContext2D::GetShadowOffsetY(float *y)
01439 {
01440     *y = 0.0f;
01441     return NS_OK;
01442 }
01443 
01444 NS_IMETHODIMP
01445 nsCanvasRenderingContext2D::SetShadowBlur(float blur)
01446 {
01447     if (!FloatValidate(blur))
01448         return NS_ERROR_DOM_SYNTAX_ERR;
01449     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
01450     return NS_OK;
01451 }
01452 
01453 NS_IMETHODIMP
01454 nsCanvasRenderingContext2D::GetShadowBlur(float *blur)
01455 {
01456     *blur = 0.0f;
01457     return NS_OK;
01458 }
01459 
01460 NS_IMETHODIMP
01461 nsCanvasRenderingContext2D::SetShadowColor(const nsAString& color)
01462 {
01463     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
01464     return NS_OK;
01465 }
01466 
01467 NS_IMETHODIMP
01468 nsCanvasRenderingContext2D::GetShadowColor(nsAString& color)
01469 {
01470     color.SetIsVoid(PR_TRUE);
01471     return NS_OK;
01472 }
01473 
01474 //
01475 // rects
01476 //
01477 
01478 NS_IMETHODIMP
01479 nsCanvasRenderingContext2D::ClearRect(float x, float y, float w, float h)
01480 {
01481     if (!FloatValidate(x,y,w,h))
01482         return NS_ERROR_DOM_SYNTAX_ERR;
01483 
01484     cairo_save (mCairo);
01485     cairo_set_operator (mCairo, CAIRO_OPERATOR_CLEAR);
01486     cairo_new_path (mCairo);
01487     cairo_rectangle (mCairo, x, y, w, h);
01488     cairo_fill (mCairo);
01489     cairo_restore (mCairo);
01490 
01491     return Redraw();
01492 }
01493 
01494 NS_IMETHODIMP
01495 nsCanvasRenderingContext2D::FillRect(float x, float y, float w, float h)
01496 {
01497     if (!FloatValidate(x,y,w,h))
01498         return NS_ERROR_DOM_SYNTAX_ERR;
01499 
01500     cairo_new_path (mCairo);
01501     cairo_rectangle (mCairo, x, y, w, h);
01502 
01503     ApplyStyle(STYLE_FILL);
01504     cairo_fill (mCairo);
01505 
01506     return Redraw();
01507 }
01508 
01509 NS_IMETHODIMP
01510 nsCanvasRenderingContext2D::StrokeRect(float x, float y, float w, float h)
01511 {
01512     if (!FloatValidate(x,y,w,h))
01513         return NS_ERROR_DOM_SYNTAX_ERR;
01514 
01515     cairo_new_path (mCairo);
01516     cairo_rectangle (mCairo, x, y, w, h);
01517 
01518     ApplyStyle(STYLE_STROKE);
01519     cairo_stroke (mCairo);
01520 
01521     return Redraw();
01522 }
01523 
01524 //
01525 // path bits
01526 //
01527 
01528 NS_IMETHODIMP
01529 nsCanvasRenderingContext2D::BeginPath()
01530 {
01531     cairo_new_path(mCairo);
01532     return NS_OK;
01533 }
01534 
01535 NS_IMETHODIMP
01536 nsCanvasRenderingContext2D::ClosePath()
01537 {
01538     cairo_close_path(mCairo);
01539     return NS_OK;
01540 }
01541 
01542 NS_IMETHODIMP
01543 nsCanvasRenderingContext2D::Fill()
01544 {
01545     ApplyStyle(STYLE_FILL);
01546     cairo_fill_preserve(mCairo);
01547     return Redraw();
01548 }
01549 
01550 NS_IMETHODIMP
01551 nsCanvasRenderingContext2D::Stroke()
01552 {
01553     ApplyStyle(STYLE_STROKE);
01554     cairo_stroke_preserve(mCairo);
01555     return Redraw();
01556 }
01557 
01558 NS_IMETHODIMP
01559 nsCanvasRenderingContext2D::Clip()
01560 {
01561     cairo_clip_preserve(mCairo);
01562     return Redraw();
01563 }
01564 
01565 NS_IMETHODIMP
01566 nsCanvasRenderingContext2D::MoveTo(float x, float y)
01567 {
01568     if (!FloatValidate(x,y))
01569         return NS_ERROR_DOM_SYNTAX_ERR;
01570 
01571     cairo_move_to(mCairo, x, y);
01572     return NS_OK;
01573 }
01574 
01575 NS_IMETHODIMP
01576 nsCanvasRenderingContext2D::LineTo(float x, float y)
01577 {
01578     if (!FloatValidate(x,y))
01579         return NS_ERROR_DOM_SYNTAX_ERR;
01580 
01581     cairo_line_to(mCairo, x, y);
01582     return NS_OK;
01583 }
01584 
01585 NS_IMETHODIMP
01586 nsCanvasRenderingContext2D::QuadraticCurveTo(float cpx, float cpy, float x, float y)
01587 {
01588     if (!FloatValidate(cpx,cpy,x,y))
01589         return NS_ERROR_DOM_SYNTAX_ERR;
01590 
01591     double cx, cy;
01592 
01593     // we will always have a current point, since beginPath forces
01594     // a moveto(0,0)
01595     cairo_get_current_point(mCairo, &cx, &cy);
01596     cairo_curve_to(mCairo,
01597                    (cx + cpx * 2.0) / 3.0,
01598                    (cy + cpy * 2.0) / 3.0,
01599                    (cpx * 2.0 + x) / 3.0,
01600                    (cpy * 2.0 + y) / 3.0,
01601                    x,
01602                    y);
01603 
01604     return NS_OK;
01605 }
01606 
01607 NS_IMETHODIMP
01608 nsCanvasRenderingContext2D::BezierCurveTo(float cp1x, float cp1y,
01609                                           float cp2x, float cp2y,
01610                                           float x, float y)
01611 {
01612     if (!FloatValidate(cp1x,cp1y,cp2x,cp2y,x,y))
01613         return NS_ERROR_DOM_SYNTAX_ERR;
01614 
01615     cairo_curve_to(mCairo, cp1x, cp1y, cp2x, cp2y, x, y);
01616     return NS_OK;
01617 }
01618 
01619 NS_IMETHODIMP
01620 nsCanvasRenderingContext2D::ArcTo(float x1, float y1, float x2, float y2, float radius)
01621 {
01622     if (!FloatValidate(x1,y1,x2,y2,radius))
01623         return NS_ERROR_DOM_SYNTAX_ERR;
01624 
01625     if (radius <= 0)
01626         return NS_ERROR_DOM_INDEX_SIZE_ERR;
01627 
01628     /* This is an adaptation of the cairo_arc_to patch from Behdad
01629      * Esfahbod; once that patch is accepted, we should remove this
01630      * and just call cairo_arc_to() directly.
01631      */
01632     
01633     double x0, y0;
01634     double angle0, angle1, angle2, angled;
01635     double d0, d2;
01636     double sin_, cos_;
01637     double xc, yc, dc;
01638     int forward;
01639 
01640     cairo_get_current_point(mCairo, &x0, &y0);
01641 
01642     angle0 = atan2 (y0 - y1, x0 - x1); /* angle from (x1,y1) to (x0,y0) */
01643     angle2 = atan2 (y2 - y1, x2 - x1); /* angle from (x1,y1) to (x2,y2) */
01644     angle1 = (angle0 + angle2) / 2;    /* angle from (x1,y1) to (xc,yc) */
01645 
01646     angled = angle2 - angle0;          /* the angle (x0,y0)--(x1,y1)--(x2,y2) */
01647 
01648     /* Shall we go forward or backward? */
01649     if (angled > M_PI || (angled < 0 && angled > -M_PI)) {
01650         angle1 += M_PI;
01651         angled = 2 * M_PI - angled;
01652         forward = 1;
01653     } else {
01654         double tmp;
01655         tmp = angle0;
01656         angle0 = angle2;
01657         angle2 = tmp;
01658         forward = 0;
01659     }
01660 
01661     angle0 += M_PI_2; /* angle from (xc,yc) to (x0,y0) */
01662     angle2 -= M_PI_2; /* angle from (xc,yc) to (x2,y2) */
01663     angled /= 2;      /* the angle (x0,y0)--(x1,y1)--(xc,yc) */
01664 
01665 
01666     /* distance from (x1,y1) to (x0,y0) */
01667     d0 = sqrt ((x0-x1)*(x0-x1)+(y0-y1)*(y0-y1));
01668     /* distance from (x2,y2) to (x0,y0) */
01669     d2 = sqrt ((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
01670 
01671     dc = -1;
01672     sin_ = sin(angled);
01673     cos_ = cos(angled);
01674     if (fabs(cos_) >= 1e-5) { /* the arc may not fit */
01675         /* min distance of end-points from corner */
01676         double min_d = d0 < d2 ? d0 : d2;
01677         /* max radius of an arc that fits */
01678         double max_r = min_d * sin_ / cos_;
01679 
01680         if (radius > max_r) {
01681             /* arc with requested radius doesn't fit */
01682             radius = (float) max_r;
01683             dc = min_d / cos_; /* distance of (xc,yc) from (x1,y1) */
01684         }
01685     }
01686 
01687     if (dc < 0)
01688         dc = radius / sin_; /* distance of (xc,yc) from (x1,y1) */
01689 
01690 
01691     /* find (cx,cy), the center of the arc */
01692     xc = x1 + sin(angle1) * dc;
01693     yc = y1 + cos(angle1) * dc;
01694 
01695 
01696     /* the arc operation draws the line from current point (x0,y0)
01697      * to arc center too. */
01698 
01699     if (forward)
01700         cairo_arc (mCairo, xc, yc, radius, angle0, angle2);
01701     else
01702         cairo_arc_negative (mCairo, xc, yc, radius, angle2, angle0);
01703 
01704     cairo_line_to (mCairo, x2, y2);
01705 
01706     return NS_OK;
01707 }
01708 
01709 NS_IMETHODIMP
01710 nsCanvasRenderingContext2D::Arc(float x, float y, float r, float startAngle, float endAngle, int ccw)
01711 {
01712     if (!FloatValidate(x,y,r,startAngle,endAngle))
01713         return NS_ERROR_DOM_SYNTAX_ERR;
01714 
01715     if (ccw)
01716         cairo_arc_negative (mCairo, x, y, r, startAngle, endAngle);
01717     else
01718         cairo_arc (mCairo, x, y, r, startAngle, endAngle);
01719     return NS_OK;
01720 }
01721 
01722 NS_IMETHODIMP
01723 nsCanvasRenderingContext2D::Rect(float x, float y, float w, float h)
01724 {
01725     if (!FloatValidate(x,y,w,h))
01726         return NS_ERROR_DOM_SYNTAX_ERR;
01727 
01728     cairo_rectangle (mCairo, x, y, w, h);
01729     return NS_OK;
01730 }
01731 
01732 
01733 //
01734 // line caps/joins
01735 //
01736 NS_IMETHODIMP
01737 nsCanvasRenderingContext2D::SetLineWidth(float width)
01738 {
01739     if (!FloatValidate(width))
01740         return NS_ERROR_DOM_SYNTAX_ERR;
01741 
01742     cairo_set_line_width(mCairo, width);
01743     return NS_OK;
01744 }
01745 
01746 NS_IMETHODIMP
01747 nsCanvasRenderingContext2D::GetLineWidth(float *width)
01748 {
01749     double d = cairo_get_line_width(mCairo);
01750     *width = (float) d;
01751     return NS_OK;
01752 }
01753 
01754 NS_IMETHODIMP
01755 nsCanvasRenderingContext2D::SetLineCap(const nsAString& capstyle)
01756 {
01757     cairo_line_cap_t cap;
01758 
01759     if (capstyle.EqualsLiteral("butt"))
01760         cap = CAIRO_LINE_CAP_BUTT;
01761     else if (capstyle.EqualsLiteral("round"))
01762         cap = CAIRO_LINE_CAP_ROUND;
01763     else if (capstyle.EqualsLiteral("square"))
01764         cap = CAIRO_LINE_CAP_SQUARE;
01765     else
01766         // XXX ERRMSG we need to report an error to developers here! (bug 329026)
01767         return NS_ERROR_NOT_IMPLEMENTED;
01768 
01769     cairo_set_line_cap (mCairo, cap);
01770     return NS_OK;
01771 }
01772 
01773 NS_IMETHODIMP
01774 nsCanvasRenderingContext2D::GetLineCap(nsAString& capstyle)
01775 {
01776     cairo_line_cap_t cap = cairo_get_line_cap(mCairo);
01777 
01778     if (cap == CAIRO_LINE_CAP_BUTT)
01779         capstyle.AssignLiteral("butt");
01780     else if (cap == CAIRO_LINE_CAP_ROUND)
01781         capstyle.AssignLiteral("round");
01782     else if (cap == CAIRO_LINE_CAP_SQUARE)
01783         capstyle.AssignLiteral("square");
01784     else
01785         return NS_ERROR_FAILURE;
01786 
01787     return NS_OK;
01788 }
01789 
01790 NS_IMETHODIMP
01791 nsCanvasRenderingContext2D::SetLineJoin(const nsAString& joinstyle)
01792 {
01793     cairo_line_join_t j;
01794 
01795     if (joinstyle.EqualsLiteral("round"))
01796         j = CAIRO_LINE_JOIN_ROUND;
01797     else if (joinstyle.EqualsLiteral("bevel"))
01798         j = CAIRO_LINE_JOIN_BEVEL;
01799     else if (joinstyle.EqualsLiteral("miter"))
01800         j = CAIRO_LINE_JOIN_MITER;
01801     else
01802         // XXX ERRMSG we need to report an error to developers here! (bug 329026)
01803         return NS_ERROR_NOT_IMPLEMENTED;
01804 
01805     cairo_set_line_join (mCairo, j);
01806     return NS_OK;
01807 }
01808 
01809 NS_IMETHODIMP
01810 nsCanvasRenderingContext2D::GetLineJoin(nsAString& joinstyle)
01811 {
01812     cairo_line_join_t j = cairo_get_line_join(mCairo);
01813 
01814     if (j == CAIRO_LINE_JOIN_ROUND)
01815         joinstyle.AssignLiteral("round");
01816     else if (j == CAIRO_LINE_JOIN_BEVEL)
01817         joinstyle.AssignLiteral("bevel");
01818     else if (j == CAIRO_LINE_JOIN_MITER)
01819         joinstyle.AssignLiteral("miter");
01820     else
01821         return NS_ERROR_FAILURE;
01822 
01823     return NS_OK;
01824 }
01825 
01826 NS_IMETHODIMP
01827 nsCanvasRenderingContext2D::SetMiterLimit(float miter)
01828 {
01829     if (!FloatValidate(miter))
01830         return NS_ERROR_DOM_SYNTAX_ERR;
01831 
01832     cairo_set_miter_limit(mCairo, miter);
01833     return NS_OK;
01834 }
01835 
01836 NS_IMETHODIMP
01837 nsCanvasRenderingContext2D::GetMiterLimit(float *miter)
01838 {
01839     double d = cairo_get_miter_limit(mCairo);
01840     *miter = (float) d;
01841     return NS_OK;
01842 }
01843 
01844 NS_IMETHODIMP
01845 nsCanvasRenderingContext2D::IsPointInPath(float x, float y, PRBool *retVal)
01846 {
01847     if (!FloatValidate(x,y))
01848         return NS_ERROR_DOM_SYNTAX_ERR;
01849 
01850     *retVal = (PRBool) cairo_in_fill(mCairo, x, y);
01851     return NS_OK;
01852 }
01853 
01854 //
01855 // image
01856 //
01857 
01858 // drawImage(in HTMLImageElement image, in float dx, in float dy);
01859 //   -- render image from 0,0 at dx,dy top-left coords
01860 // drawImage(in HTMLImageElement image, in float dx, in float dy, in float sw, in float sh);
01861 //   -- render image from 0,0 at dx,dy top-left coords clipping it to sw,sh
01862 // drawImage(in HTMLImageElement image, in float sx, in float sy, in float sw, in float sh, in float dx, in float dy, in float dw, in float dh);
01863 //   -- render the region defined by (sx,sy,sw,wh) in image-local space into the region (dx,dy,dw,dh) on the canvas
01864 
01865 NS_IMETHODIMP
01866 nsCanvasRenderingContext2D::DrawImage()
01867 {
01868     nsresult rv;
01869 
01870     if (!mCanvasElement)
01871         return NS_ERROR_FAILURE;
01872 
01873     nsCOMPtr<nsIXPCNativeCallContext> ncc;
01874     rv = nsContentUtils::XPConnect()->
01875         GetCurrentNativeCallContext(getter_AddRefs(ncc));
01876     NS_ENSURE_SUCCESS(rv, rv);
01877 
01878     if (!ncc)
01879         return NS_ERROR_FAILURE;
01880 
01881     JSContext *ctx = nsnull;
01882 
01883     rv = ncc->GetJSContext(&ctx);
01884     NS_ENSURE_SUCCESS(rv, rv);
01885 
01886     PRUint32 argc;
01887     jsval *argv = nsnull;
01888 
01889     ncc->GetArgc(&argc);
01890     ncc->GetArgvPtr(&argv);
01891 
01892     // we always need at least an image and a dx,dy
01893     if (argc < 3)
01894         return NS_ERROR_INVALID_ARG;
01895 
01896     double sx,sy,sw,sh;
01897     double dx,dy,dw,dh;
01898 
01899     nsCOMPtr<nsIDOMElement> imgElt;
01900     if (!ConvertJSValToXPCObject(getter_AddRefs(imgElt),
01901                                  NS_GET_IID(nsIDOMElement),
01902                                  ctx, argv[0]))
01903         return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
01904 
01905     cairo_surface_t *imgSurf = nsnull;
01906     cairo_matrix_t surfMat;
01907     cairo_pattern_t* pat;
01908     PRUint8 *imgData = nsnull;
01909     PRInt32 imgWidth, imgHeight;
01910     nsCOMPtr<nsIURI> uri;
01911     PRBool forceWriteOnly = PR_FALSE;
01912     rv = CairoSurfaceFromElement(imgElt, &imgSurf, &imgData,
01913                                  &imgWidth, &imgHeight, getter_AddRefs(uri), &forceWriteOnly);
01914     if (NS_FAILED(rv))
01915         return rv;
01916     DoDrawImageSecurityCheck(uri, forceWriteOnly);
01917 
01918 #define GET_ARG(dest,whicharg) \
01919     do { if (!ConvertJSValToDouble(dest, ctx, whicharg)) { rv = NS_ERROR_INVALID_ARG; goto FAIL; } } while (0)
01920 
01921     rv = NS_OK;
01922 
01923     if (argc == 3) {
01924         GET_ARG(&dx, argv[1]);
01925         GET_ARG(&dy, argv[2]);
01926         sx = sy = 0.0;
01927         dw = sw = (double) imgWidth;
01928         dh = sh = (double) imgHeight;
01929     } else if (argc == 5) {
01930         GET_ARG(&dx, argv[1]);
01931         GET_ARG(&dy, argv[2]);
01932         GET_ARG(&dw, argv[3]);
01933         GET_ARG(&dh, argv[4]);
01934         sx = sy = 0.0;
01935         sw = (double) imgWidth;
01936         sh = (double) imgHeight;
01937     } else if (argc == 9) {
01938         GET_ARG(&sx, argv[1]);
01939         GET_ARG(&sy, argv[2]);
01940         GET_ARG(&sw, argv[3]);
01941         GET_ARG(&sh, argv[4]);
01942         GET_ARG(&dx, argv[5]);
01943         GET_ARG(&dy, argv[6]);
01944         GET_ARG(&dw, argv[7]);
01945         GET_ARG(&dh, argv[8]);
01946     } else {
01947         // XXX ERRMSG we need to report an error to developers here! (bug 329026)
01948         rv = NS_ERROR_INVALID_ARG;
01949         goto FAIL;
01950     }
01951 #undef GET_ARG
01952 
01953     if (!FloatValidate(sx,sy,sw,sh))
01954         return NS_ERROR_DOM_SYNTAX_ERR;
01955     if (!FloatValidate(dx,dy,dw,dh))
01956         return NS_ERROR_DOM_SYNTAX_ERR;
01957 
01958     // check args
01959     if (sx < 0.0 || sy < 0.0 ||
01960         sw < 0.0 || sw > (double) imgWidth ||
01961         sh < 0.0 || sh > (double) imgHeight ||
01962         dw < 0.0 || dh < 0.0)
01963     {
01964         // XXX ERRMSG we need to report an error to developers here! (bug 329026)
01965         rv = NS_ERROR_DOM_INDEX_SIZE_ERR;
01966         goto FAIL;
01967     }
01968 
01969     cairo_matrix_init_translate(&surfMat, sx, sy);
01970     cairo_matrix_scale(&surfMat, sw/dw, sh/dh);
01971     pat = cairo_pattern_create_for_surface(imgSurf);
01972     cairo_pattern_set_matrix(pat, &surfMat);
01973 
01974     cairo_save(mCairo);
01975     cairo_translate(mCairo, dx, dy);
01976     cairo_new_path(mCairo);
01977     cairo_rectangle(mCairo, 0, 0, dw, dh);
01978     cairo_set_source(mCairo, pat);
01979     cairo_clip(mCairo);
01980     cairo_paint_with_alpha(mCairo, CurrentState().globalAlpha);
01981     cairo_restore(mCairo);
01982 
01983 #if 0
01984     // Disabled workaround because of a fix in cairo-win32-surface.
01985 
01986     // XXX cairo bug workaround; force a clip update on mCairo.
01987     // Otherwise, a pixman clip gets left around somewhere, and pixman
01988     // (Render) does source clipping as well -- so we end up
01989     // compositing with an incorrect clip.  This only seems to affect
01990     // fallback cases, which happen when we have CSS scaling going on.
01991     // This will blow away the current path, but we already blew it
01992     // away in this function earlier.
01993     cairo_new_path(mCairo);
01994     cairo_rectangle(mCairo, 0, 0, 0, 0);
01995     cairo_fill(mCairo);
01996 #endif
01997 
01998     cairo_pattern_destroy(pat);
01999 
02000 FAIL:
02001     if (imgData)
02002         nsMemory::Free(imgData);
02003     if (imgSurf)
02004         cairo_surface_destroy(imgSurf);
02005 
02006     if (NS_SUCCEEDED(rv))
02007         rv = Redraw();
02008 
02009     return rv;
02010 }
02011 
02012 NS_IMETHODIMP
02013 nsCanvasRenderingContext2D::SetGlobalCompositeOperation(const nsAString& op)
02014 {
02015     cairo_operator_t cairo_op;
02016 
02017 #define CANVAS_OP_TO_CAIRO_OP(cvsop,cairoop) \
02018     if (op.EqualsLiteral(cvsop))   \
02019         cairo_op = CAIRO_OPERATOR_##cairoop;
02020 
02021     // XXX "darker" isn't really correct
02022     CANVAS_OP_TO_CAIRO_OP("clear", CLEAR)
02023     else CANVAS_OP_TO_CAIRO_OP("copy", SOURCE)
02024     else CANVAS_OP_TO_CAIRO_OP("darker", SATURATE)  // XXX
02025     else CANVAS_OP_TO_CAIRO_OP("destination-atop", DEST_ATOP)
02026     else CANVAS_OP_TO_CAIRO_OP("destination-in", DEST_IN)
02027     else CANVAS_OP_TO_CAIRO_OP("destination-out", DEST_OUT)
02028     else CANVAS_OP_TO_CAIRO_OP("destination-over", DEST_OVER)
02029     else CANVAS_OP_TO_CAIRO_OP("lighter", ADD)
02030     else CANVAS_OP_TO_CAIRO_OP("source-atop", ATOP)
02031     else CANVAS_OP_TO_CAIRO_OP("source-in", IN)
02032     else CANVAS_OP_TO_CAIRO_OP("source-out", OUT)
02033     else CANVAS_OP_TO_CAIRO_OP("source-over", OVER)
02034     else CANVAS_OP_TO_CAIRO_OP("xor", XOR)
02035     // not part of spec, kept here for compat
02036     else CANVAS_OP_TO_CAIRO_OP("over", OVER)
02037     else return NS_ERROR_NOT_IMPLEMENTED;
02038 
02039 #undef CANVAS_OP_TO_CAIRO_OP
02040 
02041     cairo_set_operator(mCairo, cairo_op);
02042     return NS_OK;
02043 }
02044 
02045 NS_IMETHODIMP
02046 nsCanvasRenderingContext2D::GetGlobalCompositeOperation(nsAString& op)
02047 {
02048     cairo_operator_t cairo_op = cairo_get_operator(mCairo);
02049 
02050 #define CANVAS_OP_TO_CAIRO_OP(cvsop,cairoop) \
02051     if (cairo_op == CAIRO_OPERATOR_##cairoop) \
02052         op.AssignLiteral(cvsop);
02053 
02054     // XXX "darker" isn't really correct
02055     CANVAS_OP_TO_CAIRO_OP("clear", CLEAR)
02056     else CANVAS_OP_TO_CAIRO_OP("copy", SOURCE)
02057     else CANVAS_OP_TO_CAIRO_OP("darker", SATURATE)  // XXX
02058     else CANVAS_OP_TO_CAIRO_OP("destination-atop", DEST_ATOP)
02059     else CANVAS_OP_TO_CAIRO_OP("destination-in", DEST_IN)
02060     else CANVAS_OP_TO_CAIRO_OP("destination-out", DEST_OUT)
02061     else CANVAS_OP_TO_CAIRO_OP("destination-over", DEST_OVER)
02062     else CANVAS_OP_TO_CAIRO_OP("lighter", ADD)
02063     else CANVAS_OP_TO_CAIRO_OP("source-atop", ATOP)
02064     else CANVAS_OP_TO_CAIRO_OP("source-in", IN)
02065     else CANVAS_OP_TO_CAIRO_OP("source-out", OUT)
02066     else CANVAS_OP_TO_CAIRO_OP("source-over", OVER)
02067     else CANVAS_OP_TO_CAIRO_OP("xor", XOR)
02068     else return NS_ERROR_FAILURE;
02069 
02070 #undef CANVAS_OP_TO_CAIRO_OP
02071 
02072     return NS_OK;
02073 }
02074 
02075 
02076 //
02077 // Utils
02078 //
02079 PRBool
02080 nsCanvasRenderingContext2D::ConvertJSValToUint32(PRUint32* aProp, JSContext* aContext,
02081                                                  jsval aValue)
02082 {
02083   uint32 temp;
02084   if (::JS_ValueToECMAUint32(aContext, aValue, &temp)) {
02085     *aProp = (PRUint32)temp;
02086   }
02087   else {
02088     ::JS_ReportError(aContext, "Parameter must be an integer");
02089     return JS_FALSE;
02090   }
02091 
02092   return JS_TRUE;
02093 }
02094 
02095 PRBool
02096 nsCanvasRenderingContext2D::ConvertJSValToDouble(double* aProp, JSContext* aContext,
02097                                                  jsval aValue)
02098 {
02099   jsdouble temp;
02100   if (::JS_ValueToNumber(aContext, aValue, &temp)) {
02101     *aProp = (jsdouble)temp;
02102   }
02103   else {
02104     ::JS_ReportError(aContext, "Parameter must be a number");
02105     return JS_FALSE;
02106   }
02107 
02108   return JS_TRUE;
02109 }
02110 
02111 PRBool
02112 nsCanvasRenderingContext2D::ConvertJSValToXPCObject(nsISupports** aSupports, REFNSIID aIID,
02113                                                     JSContext* aContext, jsval aValue)
02114 {
02115   *aSupports = nsnull;
02116   if (JSVAL_IS_NULL(aValue)) {
02117     return JS_TRUE;
02118   }
02119 
02120   if (JSVAL_IS_OBJECT(aValue)) {
02121     // WrapJS does all the work to recycle an existing wrapper and/or do a QI
02122     nsresult rv = nsContentUtils::XPConnect()->
02123       WrapJS(aContext, JSVAL_TO_OBJECT(aValue), aIID, (void**)aSupports);
02124 
02125     return NS_SUCCEEDED(rv);
02126   }
02127 
02128   return JS_FALSE;
02129 }
02130 
02131 /* cairo ARGB32 surfaces are ARGB stored as a packed 32-bit integer; on little-endian
02132  * platforms, they appear as BGRA bytes in the surface data.  The color values are also
02133  * stored with premultiplied alpha.
02134  */
02135 
02136 nsresult
02137 nsCanvasRenderingContext2D::CairoSurfaceFromElement(nsIDOMElement *imgElt,
02138                                                     cairo_surface_t **aCairoSurface,
02139                                                     PRUint8 **imgData,
02140                                                     PRInt32 *widthOut, PRInt32 *heightOut,
02141                                                     nsIURI **uriOut, PRBool *forceWriteOnlyOut)
02142 {
02143     nsresult rv;
02144 
02145     nsCOMPtr<imgIContainer> imgContainer;
02146 
02147     nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(imgElt);
02148     if (imageLoader) {
02149         nsCOMPtr<imgIRequest> imgRequest;
02150         rv = imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
02151                                      getter_AddRefs(imgRequest));
02152         NS_ENSURE_SUCCESS(rv, rv);
02153         if (!imgRequest)
02154             // XXX ERRMSG we need to report an error to developers here! (bug 329026)
02155             return NS_ERROR_NOT_AVAILABLE;
02156 
02157         PRUint32 status;
02158         imgRequest->GetImageStatus(&status);
02159         if ((status & imgIRequest::STATUS_LOAD_COMPLETE) == 0)
02160             return NS_ERROR_NOT_AVAILABLE;
02161 
02162         nsCOMPtr<nsIURI> uri;
02163         rv = imageLoader->GetCurrentURI(uriOut);
02164         NS_ENSURE_SUCCESS(rv, rv);
02165 
02166         *forceWriteOnlyOut = PR_FALSE;
02167 
02168         rv = imgRequest->GetImage(getter_AddRefs(imgContainer));
02169         NS_ENSURE_SUCCESS(rv, rv);
02170     } else {
02171         // maybe a canvas
02172         nsCOMPtr<nsICanvasElement> canvas = do_QueryInterface(imgElt);
02173         if (canvas) {
02174             PRUint32 w, h;
02175             rv = canvas->GetSize(&w, &h);
02176             NS_ENSURE_SUCCESS(rv, rv);
02177 
02178             cairo_surface_t *surf =
02179                 cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
02180                                             w, h);
02181             cairo_t *cr = cairo_create (surf);
02182             cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
02183             cairo_paint (cr);
02184             cairo_destroy (cr);
02185 
02186             rv = canvas->RenderContextsToSurface(surf);
02187             if (NS_FAILED(rv)) {
02188                 cairo_surface_destroy (surf);
02189                 return rv;
02190             }
02191 
02192             *aCairoSurface = surf;
02193             *imgData = nsnull;
02194             *widthOut = w;
02195             *heightOut = h;
02196 
02197             *uriOut = nsnull;
02198             *forceWriteOnlyOut = canvas->IsWriteOnly();
02199 
02200             return NS_OK;
02201         } else {
02202             NS_WARNING("No way to get surface from non-canvas, non-imageloader");
02203             return NS_ERROR_NOT_AVAILABLE;
02204         }
02205     }
02206 
02207     if (!imgContainer)
02208         return NS_ERROR_NOT_AVAILABLE;
02209 
02210     nsCOMPtr<gfxIImageFrame> frame;
02211     rv = imgContainer->GetCurrentFrame(getter_AddRefs(frame));
02212     NS_ENSURE_SUCCESS(rv, rv);
02213 
02214     nsCOMPtr<nsIImage> img(do_GetInterface(frame));
02215 
02216     PRInt32 imgWidth, imgHeight;
02217     rv = frame->GetWidth(&imgWidth);
02218     rv |= frame->GetHeight(&imgHeight);
02219     if (NS_FAILED(rv))
02220         return NS_ERROR_FAILURE;
02221 
02222     if (widthOut)
02223         *widthOut = imgWidth;
02224     if (heightOut)
02225         *heightOut = imgHeight;
02226 
02227 #ifdef MOZ_CAIRO_GFX
02228     gfxASurface* gfxsurf = nsnull;
02229     rv = img->GetSurface(&gfxsurf);
02230     NS_ENSURE_SUCCESS(rv, rv);
02231 
02232     *aCairoSurface = gfxsurf->CairoSurface();
02233     cairo_surface_reference (*aCairoSurface);
02234     *imgData = nsnull;
02235 #else
02236     //
02237     // We now need to create a cairo_surface with the same data as
02238     // this image element.
02239     //
02240 
02241     PRUint8 *cairoImgData = (PRUint8 *)nsMemory::Alloc(imgHeight * imgWidth * 4);
02242     PRUint8 *outData = cairoImgData;
02243 
02244     gfx_format format;
02245     rv = frame->GetFormat(&format);
02246     NS_ENSURE_SUCCESS(rv, rv);
02247 
02248     rv = frame->LockImageData();
02249     if (img->GetHasAlphaMask())
02250         rv |= frame->LockAlphaData();
02251     if (NS_FAILED(rv)) {
02252         nsMemory::Free(cairoImgData);
02253         return NS_ERROR_FAILURE;
02254     }
02255 
02256     PRUint8 *inPixBits, *inAlphaBits = nsnull;
02257     PRUint32 inPixStride, inAlphaStride = 0;
02258     inPixBits = img->GetBits();
02259     inPixStride = img->GetLineStride();
02260     if (img->GetHasAlphaMask()) {
02261         inAlphaBits = img->GetAlphaBits();
02262         inAlphaStride = img->GetAlphaLineStride();
02263     }
02264 
02265     PRBool topToBottom = img->GetIsRowOrderTopToBottom();
02266     PRBool useBGR;
02267 
02268     // The gtk backend optimizes away the alpha mask of images
02269     // with a fully opaque alpha, but doesn't update its format (bug?);
02270     // you end up with a RGB_A8 image with GetHasAlphaMask() == false.
02271     // We need to treat that case as RGB.  We also need to understand
02272     // that with 1-bit alpha the data is actually RGB_A1, not RGB_A8,
02273     // no matter what the format says.
02274 #ifdef MOZ_WIDGET_GTK2
02275     PRInt8 alphaDepth = img->GetAlphaDepth();
02276     if (alphaDepth == 0) {
02277         if (format == gfxIFormats::RGB_A8)
02278             format = gfxIFormats::RGB;
02279         else if (format == gfxIFormats::BGR_A8)
02280             format = gfxIFormats::BGR;
02281     } else if (alphaDepth == 1) {
02282         if (format == gfxIFormats::RGB_A8)
02283             format = gfxIFormats::RGB_A1;
02284         else if (format == gfxIFormats::BGR_A8)
02285             format = gfxIFormats::BGR_A1;
02286     }
02287 #endif
02288 
02289     // We probably don't need the GetHasAlphaMask check here, because it
02290     // should've been handled up above, but as this patch is going into
02291     // the release branch I don't want to change this code unnecessarily.
02292     if ((format == gfxIFormats::RGB || format == gfxIFormats::BGR) ||
02293         (!(img->GetHasAlphaMask()) && (format == gfxIFormats::RGB_A8 || format == gfxIFormats::BGR_A8)))
02294     {
02295         useBGR = (format & 1);
02296 
02297 #ifdef IS_BIG_ENDIAN
02298         useBGR = !useBGR;
02299 #endif
02300 
02301         for (PRUint32 j = 0; j < (PRUint32) imgHeight; j++) {
02302             PRUint32 rowIndex;
02303             if (topToBottom)
02304                 rowIndex = j;
02305             else
02306                 rowIndex = imgHeight - j - 1;
02307 
02308             PRUint8 *inrowrgb = inPixBits + (inPixStride * rowIndex);
02309 
02310             for (PRUint32 i = 0; i < (PRUint32) imgWidth; i++) {
02311                 // handle rgb data; no alpha to premultiply
02312 #ifdef XP_MACOSX
02313                 // skip extra OSX byte
02314                 inrowrgb++;
02315 #endif
02316                 PRUint8 b = *inrowrgb++;
02317                 PRUint8 g = *inrowrgb++;
02318                 PRUint8 r = *inrowrgb++;
02319 
02320 #ifdef IS_BIG_ENDIAN
02321                 // alpha
02322                 *outData++ = 0xff;
02323 #endif
02324 
02325                 if (useBGR) {
02326                     *outData++ = b;
02327                     *outData++ = g;
02328                     *outData++ = r;
02329                 } else {
02330                     *outData++ = r;
02331                     *outData++ = g;
02332                     *outData++ = b;
02333                 }
02334 
02335 #ifdef IS_LITTLE_ENDIAN
02336                 // alpha
02337                 *outData++ = 0xff;
02338 #endif
02339             }
02340         }
02341         rv = NS_OK;
02342     } else if (format == gfxIFormats::RGB_A1 || format == gfxIFormats::BGR_A1) {
02343         useBGR = (format & 1);
02344 
02345 #ifdef IS_BIG_ENDIAN
02346         useBGR = !useBGR;
02347 #endif
02348 
02349         for (PRUint32 j = 0; j < (PRUint32) imgHeight; j++) {
02350             PRUint32 rowIndex;
02351             if (topToBottom)
02352                 rowIndex = j;
02353             else
02354                 rowIndex = imgHeight - j - 1;
02355 
02356             PRUint8 *inrowrgb = inPixBits + (inPixStride * rowIndex);
02357             PRUint8 *inrowalpha = inAlphaBits + (inAlphaStride * rowIndex);
02358 
02359             for (PRUint32 i = 0; i < (PRUint32) imgWidth; i++) {
02360                 // pull out the bit value into alpha
02361                 PRInt32 bit = i % 8;
02362                 PRInt32 byte = i / 8;
02363 
02364 #ifdef IS_LITTLE_ENDIAN
02365                 PRUint8 a = (inrowalpha[byte] >> (7-bit)) & 1;
02366 #else
02367                 PRUint8 a = (inrowalpha[byte] >> bit) & 1;
02368 #endif
02369 
02370 #ifdef XP_MACOSX
02371                 // skip extra X8 byte on OSX
02372                 inrowrgb++;
02373 #endif
02374 
02375                 // handle rgb data; need to multiply the alpha out,
02376                 // but we short-circuit that here since we know that a
02377                 // can only be 0 or 1
02378                 if (a) {
02379                     PRUint8 b = *inrowrgb++;
02380                     PRUint8 g = *inrowrgb++;
02381                     PRUint8 r = *inrowrgb++;
02382 
02383 #ifdef IS_BIG_ENDIAN
02384                     // alpha
02385                     *outData++ = 0xff;
02386 #endif
02387 
02388                     if (useBGR) {
02389                         *outData++ = b;
02390                         *outData++ = g;
02391                         *outData++ = r;
02392                     } else {
02393                         *outData++ = r;
02394                         *outData++ = g;
02395                         *outData++ = b;
02396                     }
02397 
02398 #ifdef IS_LITTLE_ENDIAN
02399                     // alpha
02400                     *outData++ = 0xff;
02401 #endif
02402                 } else {
02403                     // alpha is 0, so we need to write all 0's,
02404                     // ignoring input color
02405                     inrowrgb += 3;
02406                     *outData++ = 0;
02407                     *outData++ = 0;
02408                     *outData++ = 0;
02409                     *outData++ = 0;
02410                 }
02411             }
02412         }
02413         rv = NS_OK;
02414     } else if (format == gfxIFormats::RGB_A8 || format == gfxIFormats::BGR_A8) {
02415         useBGR = (format & 1);
02416 
02417 #ifdef IS_BIG_ENDIAN
02418         useBGR = !useBGR;
02419 #endif
02420 
02421         for (PRUint32 j = 0; j < (PRUint32) imgHeight; j++) {
02422             PRUint32 rowIndex;
02423             if (topToBottom)
02424                 rowIndex = j;
02425             else
02426                 rowIndex = imgHeight - j - 1;
02427 
02428             PRUint8 *inrowrgb = inPixBits + (inPixStride * rowIndex);
02429             PRUint8 *inrowalpha = inAlphaBits + (inAlphaStride * rowIndex);
02430 
02431             for (PRUint32 i = 0; i < (PRUint32) imgWidth; i++) {
02432                 // pull out alpha; we'll need it to premultiply
02433                 PRUint8 a = *inrowalpha++;
02434 
02435                 // handle rgb data; we need to fully premultiply
02436                 // with the alpha
02437 #ifdef XP_MACOSX
02438                 // skip extra X8 byte on OSX
02439                 inrowrgb++;
02440 #endif
02441 
02442                 // XXX gcc bug: gcc seems to push "r" into a register
02443                 // early, and pretends that it's in that register
02444                 // throughout the 3 macros below.  At the end
02445                 // of the 3rd macro, the correct r value is
02446                 // calculated but never stored anywhere -- the r variable
02447                 // has the value of the low byte of register that it
02448                 // was stuffed into, which has the result of some 
02449                 // intermediate calculation.
02450                 // I've seen this on gcc 3.4.2 x86 (Fedora Core 3)
02451                 // and gcc 3.3 PPC (OS X 10.3)
02452 
02453                 //PRUint8 b, g, r;
02454                 //FAST_DIVIDE_BY_255(b, *inrowrgb++ * a - a / 2);
02455                 //FAST_DIVIDE_BY_255(g, *inrowrgb++ * a - a / 2);
02456                 //FAST_DIVIDE_BY_255(r, *inrowrgb++ * a - a / 2);
02457 
02458                 PRUint8 b = (*inrowrgb++ * a - a / 2) / 255;
02459                 PRUint8 g = (*inrowrgb++ * a - a / 2) / 255;
02460                 PRUint8 r = (*inrowrgb++ * a - a / 2) / 255;
02461 
02462 #ifdef IS_BIG_ENDIAN
02463                 *outData++ = a;
02464 #endif
02465 
02466                 if (useBGR) {
02467                     *outData++ = b;
02468                     *outData++ = g;
02469                     *outData++ = r;
02470                 } else {
02471                     *outData++ = r;
02472                     *outData++ = g;
02473                     *outData++ = b;
02474                 }
02475 
02476 #ifdef IS_LITTLE_ENDIAN
02477                 *outData++ = a;
02478 #endif
02479             }
02480         }
02481         rv = NS_OK;
02482     } else {
02483         rv = NS_ERROR_FAILURE;
02484     }
02485 
02486     if (img->GetHasAlphaMask())
02487         frame->UnlockAlphaData();
02488     frame->UnlockImageData();
02489 
02490     if (NS_FAILED(rv)) {
02491         nsMemory::Free(cairoImgData);
02492         return rv;
02493     }
02494 
02495     cairo_surface_t *imgSurf =
02496         cairo_image_surface_create_for_data(cairoImgData, CAIRO_FORMAT_ARGB32,
02497                                             imgWidth, imgHeight, imgWidth*4);
02498 
02499     *aCairoSurface = imgSurf;
02500     *imgData = cairoImgData;
02501 #endif
02502 
02503     return NS_OK;
02504 }
02505 
02506 PRBool
02507 CheckSaneImageSize (PRInt32 width, PRInt32 height)
02508 {
02509     if (width <= 0 || height <= 0)
02510         return PR_FALSE;
02511 
02512     /* check to make sure we don't overflow a 32-bit */
02513     PRInt32 tmp = width * height;
02514     if (tmp / height != width)
02515         return PR_FALSE;
02516 
02517     tmp = tmp * 4;
02518     if (tmp / 4 != width * height)
02519         return PR_FALSE;
02520 
02521     /* reject over-wide or over-tall images */
02522     const PRInt32 kSizeLimit = 0x00007FFF;
02523     if (width > kSizeLimit || height > kSizeLimit)
02524         return PR_FALSE;
02525 
02526     return PR_TRUE;
02527 }
02528 
02529 /* Check that the rect [x,y,w,h] is a valid subrect of [0,0,realWidth,realHeight]
02530  * without overflowing any integers and the like.
02531  */
02532 PRBool
02533 CheckSaneSubrectSize (PRInt32 x, PRInt32 y, PRInt32 w, PRInt32 h, PRInt32 realWidth, PRInt32 realHeight)
02534 {
02535     if (w <= 0 || h <= 0 || x < 0 || y < 0)
02536         return PR_FALSE;
02537 
02538     if (x >= realWidth  || w > (realWidth - x) ||
02539         y >= realHeight || h > (realHeight - y))
02540         return PR_FALSE;
02541 
02542     return PR_TRUE;
02543 }
02544 
02545 NS_IMETHODIMP
02546 nsCanvasRenderingContext2D::DrawWindow(nsIDOMWindow* aWindow, PRInt32 aX, PRInt32 aY,
02547                                        PRInt32 aW, PRInt32 aH, 
02548                                        const nsAString& aBGColor)
02549 {
02550     NS_ENSURE_ARG(aWindow != nsnull);
02551 
02552     // protect against too-large surfaces that will cause allocation
02553     // or overflow issues
02554     if (!CheckSaneImageSize (aW, aH))
02555         return NS_ERROR_FAILURE;
02556 
02557     // We can't allow web apps to call this until we fix at least the
02558     // following potential security issues:
02559     // -- rendering cross-domain IFRAMEs and then extracting the results
02560     // -- rendering the user's theme and then extracting the results
02561     // -- rendering native anonymous content (e.g., file input paths;
02562     // scrollbars should be allowed)
02563     nsCOMPtr<nsIScriptSecurityManager> ssm =
02564         do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
02565     if (!ssm)
02566         return NS_ERROR_FAILURE;
02567 
02568     PRBool isTrusted = PR_FALSE;
02569     PRBool isChrome = PR_FALSE;
02570     PRBool hasCap = PR_FALSE;
02571 
02572     // The secman really should handle UniversalXPConnect case, since that
02573     // should include UniversalBrowserRead... doesn't right now, though.
02574     if ((NS_SUCCEEDED(ssm->SubjectPrincipalIsSystem(&isChrome)) && isChrome) ||
02575         (NS_SUCCEEDED(ssm->IsCapabilityEnabled("UniversalBrowserRead", &hasCap)) && hasCap) ||
02576         (NS_SUCCEEDED(ssm->IsCapabilityEnabled("UniversalXPConnect", &hasCap)) && hasCap))
02577     {
02578         isTrusted = PR_TRUE;
02579     }
02580 
02581     if (!isTrusted) {
02582         // not permitted to use DrawWindow
02583         // XXX ERRMSG we need to report an error to developers here! (bug 329026)
02584         return NS_ERROR_DOM_SECURITY_ERR;
02585     }
02586     
02587     // Flush layout updates
02588     nsCOMPtr<nsIDOMDocument> domDoc;
02589     aWindow->GetDocument(getter_AddRefs(domDoc));
02590     if (domDoc) {
02591         nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
02592         if (doc) {
02593             doc->FlushPendingNotifications(Flush_Layout);
02594         }
02595     }
02596 
02597     nsCOMPtr<nsPresContext> presContext;
02598 #ifdef MOZILLA_1_8_BRANCH
02599     nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
02600     if (sgo) {
02601         nsIDocShell* docshell = sgo->GetDocShell();
02602         if (docshell) {
02603             docshell->GetPresContext(getter_AddRefs(presContext));
02604         }
02605     }
02606 #else
02607     nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aWindow);
02608     if (win) {
02609         nsIDocShell* docshell = win->GetDocShell();
02610         if (docshell) {
02611             docshell->GetPresContext(getter_AddRefs(presContext));
02612         }
02613     }
02614 #endif
02615     if (!presContext)
02616         return NS_ERROR_FAILURE;
02617 
02618 #ifdef MOZILLA_1_8_BRANCH
02619     // Dig down past the viewport scroll stuff
02620     nsIViewManager* vm = presContext->GetViewManager();
02621     nsIView* view;
02622     vm->GetRootView(view);
02623     NS_ASSERTION(view, "Must have root view!");
02624 #endif
02625 
02626     nscolor bgColor;
02627     nsresult rv = mCSSParser->ParseColorString(PromiseFlatString(aBGColor),
02628                                                nsnull, 0, PR_TRUE, &bgColor);
02629     NS_ENSURE_SUCCESS(rv, rv);
02630     
02631     float p2t = presContext->PixelsToTwips();
02632     nsRect r(aX, aY, aW, aH);
02633     r.ScaleRoundOut(p2t);
02634 
02635 #ifndef MOZILLA_1_8_BRANCH    
02636     nsIPresShell* presShell = presContext->PresShell();
02637 #endif
02638 
02639 #ifdef MOZ_CAIRO_GFX
02640     mThebesContext->Save();
02641     //mThebesContext->NewPath();
02642     //mThebesContext->Rectangle(gfxRect(0, 0, aW, aH));
02643     //mThebesContext->Clip();
02644 
02645     mThebesContext->PushGroup(NS_GET_A(bgColor) == 0xff ? gfxContext::CONTENT_COLOR_ALPHA : gfxContext::CONTENT_COLOR_ALPHA);
02646 
02647     // draw background color
02648     if (NS_GET_A(bgColor) > 0) {
02649       mThebesContext->SetColor(gfxRGBA(bgColor));
02650       mThebesContext->SetOperator(gfxContext::OPERATOR_SOURCE);
02651       mThebesContext->Paint();
02652     }
02653 
02654     // we want the window to be composited as a single image using
02655     // whatever operator was set, so set this to the default OVER;
02656     // the original operator will be present when we PopGroup
02657     mThebesContext->SetOperator(gfxContext::OPERATOR_OVER);
02658 
02659     nsIFrame* rootFrame = presShell->FrameManager()->GetRootFrame();
02660     if (0 && rootFrame) {
02661         nsRect r(aX, aY, aW, aH);
02662         r.ScaleRoundOut(presContext->PixelsToTwips());
02663 
02664         nsDisplayListBuilder builder(rootFrame, PR_FALSE, PR_FALSE);
02665         nsDisplayList list;
02666         nsIScrollableView* scrollingView = nsnull;       
02667         presContext->GetViewManager()->GetRootScrollableView(&scrollingView);
02668 
02669         if (scrollingView) {
02670             nscoord x, y;
02671             scrollingView->GetScrollPosition(x, y);
02672             r.MoveBy(-x, -y);
02673             builder.SetIgnoreScrollFrame(presShell->GetRootScrollFrame());
02674         }
02675 
02676         rv = rootFrame->BuildDisplayListForStackingContext(&builder, r, &list);      
02677         if (NS_SUCCEEDED(rv)) {
02678             float t2p = presContext->TwipsToPixels();
02679             // Ensure that r.x,r.y gets drawn at (0,0)
02680             mThebesContext->Save();
02681             mThebesContext->Translate(gfxPoint(-r.x*t2p, -r.y*t2p));
02682           
02683             nsIDeviceContext* devCtx = presContext->DeviceContext();
02684             nsCOMPtr<nsIRenderingContext> rc;
02685             devCtx->CreateRenderingContextInstance(*getter_AddRefs(rc));
02686             rc->Init(devCtx, mThebesContext);
02687             
02688             nsRegion region(r);
02689             list.OptimizeVisibility(&builder, &region);
02690             list.Paint(&builder, rc, r);
02691             // Flush the list so we don't trigger the IsEmpty-on-destruction assertion
02692             list.DeleteAll();
02693 
02694             mThebesContext->Restore();
02695         }
02696     }
02697 
02698     mThebesContext->PopGroupToSource();
02699     mThebesContext->Paint();
02700     mThebesContext->Restore();
02701 
02702     // get rid of the pattern surface ref, just in case
02703     cairo_set_source_rgba (mCairo, 1, 1, 1, 1);
02704     DirtyAllStyles();
02705 
02706     Redraw();
02707 #else
02708 
02709     nsCOMPtr<nsIRenderingContext> blackCtx;
02710 #ifdef MOZILLA_1_8_BRANCH
02711     rv = vm->RenderOffscreen(view, r, PR_FALSE, PR_TRUE,
02712                              NS_ComposeColors(NS_RGB(0, 0, 0), bgColor),
02713                              getter_AddRefs(blackCtx));
02714 #else
02715     rv = presShell->RenderOffscreen(r, PR_FALSE, PR_TRUE,
02716                                     NS_ComposeColors(NS_RGB(0, 0, 0), bgColor),
02717                                     getter_AddRefs(blackCtx));
02718 #endif
02719     NS_ENSURE_SUCCESS(rv, rv);
02720     
02721     nsIDrawingSurface* blackSurface;
02722     blackCtx->GetDrawingSurface(&blackSurface);
02723     if (!blackSurface)
02724         return NS_ERROR_FAILURE;
02725     
02726     // Render it!
02727     if (NS_GET_A(bgColor) == 0xFF) {
02728         // opaque background. Do it the easy way.
02729         rv = DrawNativeSurfaces(blackSurface, nsnull, nsSize(aW, aH), blackCtx);
02730         blackCtx->DestroyDrawingSurface(blackSurface);
02731         return rv;
02732     }
02733     
02734     // transparent background. Do it the hard way. We've drawn onto black,
02735     // now draw onto white so we can recover the translucency information.
02736     // But we need to compose our given background color onto black/white
02737     // to get the real background to use.
02738     nsCOMPtr<nsIRenderingContext> whiteCtx;
02739 #ifdef MOZILLA_1_8_BRANCH
02740     rv = vm->RenderOffscreen(view, r, PR_FALSE, PR_TRUE,
02741                              NS_ComposeColors(NS_RGB(255, 255, 255), bgColor),
02742                              getter_AddRefs(whiteCtx));
02743 #else
02744     rv = presShell->RenderOffscreen(r, PR_FALSE, PR_TRUE,
02745                                     NS_ComposeColors(NS_RGB(255, 255, 255), bgColor),
02746                                     getter_AddRefs(whiteCtx));
02747 #endif
02748     if (NS_SUCCEEDED(rv)) {
02749         nsIDrawingSurface* whiteSurface;
02750         whiteCtx->GetDrawingSurface(&whiteSurface);
02751         if (!whiteSurface) {
02752             rv = NS_ERROR_FAILURE;
02753         } else {
02754             rv = DrawNativeSurfaces(blackSurface, whiteSurface, nsSize(aW, aH), blackCtx);
02755             whiteCtx->DestroyDrawingSurface(whiteSurface);
02756         }
02757     }
02758     
02759     blackCtx->DestroyDrawingSurface(blackSurface);
02760 #endif
02761 
02762     return rv;
02763 }
02764 
02772 static PRUint32 ComputeScaleFactor(PRUint32 aBits)
02773 {
02774   static PRUint32 table[9] = {
02775     0, 255*256, 85*256, 9330, 17*256, 2110, 1038, 515, 256
02776   };
02777   
02778   NS_ASSERTION(aBits <= 8, "more than 8 bits in a color channel not supported");
02779   NS_ASSERTION(((table[aBits]*((1 << aBits) - 1)) >> 8) == 255,
02780                "Invalid table entry");
02781   return table[aBits];
02782 }
02783 
02784 nsresult
02785 nsCanvasRenderingContext2D::DrawNativeSurfaces(nsIDrawingSurface* aBlackSurface,
02786                                                nsIDrawingSurface* aWhiteSurface,
02787                                                const nsIntSize& aSurfaceSize,
02788                                                nsIRenderingContext* aBlackContext)
02789 {
02790     // check if the dimensions are too large;
02791     // if they are, we may easily overflow malloc later on
02792     if (!CheckSaneImageSize (aSurfaceSize.width, aSurfaceSize.height))
02793         return NS_ERROR_FAILURE;
02794 
02795     // Acquire alpha values
02796     nsAutoArrayPtr<PRUint8> alphas;
02797     nsresult rv;
02798     if (aWhiteSurface) {
02799         // There is transparency. Use the blender to recover alphas.
02800         nsCOMPtr<nsIBlender> blender = do_CreateInstance(kBlenderCID, &rv);
02801         NS_ENSURE_SUCCESS(rv, rv);
02802         nsIDeviceContext* dc = nsnull;
02803         aBlackContext->GetDeviceContext(dc);
02804         rv = blender->Init(dc);
02805         NS_ENSURE_SUCCESS(rv, rv);
02806         
02807         rv = blender->GetAlphas(nsRect(0, 0, aSurfaceSize.width, aSurfaceSize.height),
02808                                 aBlackSurface, aWhiteSurface, getter_Transfers(alphas));
02809         NS_ENSURE_SUCCESS(rv, rv);
02810     }
02811 
02812     // We use aBlackSurface to get the image color data
02813     PRUint8* data;
02814     PRInt32 rowLen, rowSpan;
02815     rv = aBlackSurface->Lock(0, 0, aSurfaceSize.width, aSurfaceSize.height,
02816                              (void**)&data, &rowSpan, &rowLen,
02817                              NS_LOCK_SURFACE_READ_ONLY);
02818     if (NS_FAILED(rv))
02819         return rv;
02820 
02821     // Get info about native surface layout
02822     PRUint32 bytesPerPix = rowLen/aSurfaceSize.width;
02823     nsPixelFormat format;
02824     
02825 #ifndef XP_MACOSX
02826     rv = aBlackSurface->GetPixelFormat(&format);
02827     if (NS_FAILED(rv)) {
02828         aBlackSurface->Unlock();
02829         return rv;
02830     }
02831 #else
02832     // On the mac, GetPixelFormat returns NS_ERROR_NOT_IMPLEMENTED;
02833     // we fake the pixel format here.  The data that we care about
02834     // will be in ABGR format, either 8-8-8 or 5-5-5.
02835 
02836     if (bytesPerPix == 4) {
02837         format.mRedZeroMask   = 0xff;
02838         format.mGreenZeroMask = 0xff;
02839         format.mBlueZeroMask  = 0xff;
02840         format.mAlphaZeroMask = 0;
02841         
02842         format.mRedMask   = 0x00ff0000;
02843         format.mGreenMask = 0x0000ff00;
02844         format.mBlueMask  = 0x000000ff;
02845         format.mAlphaMask = 0;
02846         
02847         format.mRedCount   = 8;
02848         format.mGreenCount = 8;
02849         format.mBlueCount  = 8;
02850         format.mAlphaCount = 0;
02851         
02852         format.mRedShift   = 16;
02853         format.mGreenShift = 8;
02854         format.mBlueShift  = 0;
02855         format.mAlphaShift = 0;
02856     } else if (bytesPerPix == 2) {
02857         format.mRedZeroMask   = 0x1f;
02858         format.mGreenZeroMask = 0x1f;
02859         format.mBlueZeroMask  = 0x1f;
02860         format.mAlphaZeroMask = 0;
02861         
02862         format.mRedMask   = 0x7C00;
02863         format.mGreenMask = 0x03E0;
02864         format.mBlueMask  = 0x001F;
02865         format.mAlphaMask = 0;
02866         
02867         format.mRedCount   = 5;
02868         format.mGreenCount = 5;
02869         format.mBlueCount  = 5;
02870         format.mAlphaCount = 0;
02871         
02872         format.mRedShift   = 10;
02873         format.mGreenShift = 5;
02874         format.mBlueShift  = 0;
02875         format.mAlphaShift = 0;
02876     } else {
02877         // no clue!
02878         aBlackSurface->Unlock();
02879         return NS_ERROR_FAILURE;
02880     }
02881     
02882 #endif
02883 
02884     // Create a temporary surface to hold the full-size image in cairo
02885     // image format.
02886     nsAutoArrayPtr<PRUint8> tmpBuf(new PRUint8[aSurfaceSize.width*aSurfaceSize.height*4]);
02887     if (!tmpBuf) {
02888         aBlackSurface->Unlock();
02889         return NS_ERROR_OUT_OF_MEMORY;
02890     }
02891 
02892     cairo_surface_t *tmpSurf =
02893         cairo_image_surface_create_for_data(tmpBuf.get(),
02894                                             CAIRO_FORMAT_ARGB32, aSurfaceSize.width, aSurfaceSize.height,
02895                                             aSurfaceSize.width*4);
02896     if (!tmpSurf) {
02897         aBlackSurface->Unlock();
02898         return NS_ERROR_OUT_OF_MEMORY;
02899     }
02900 
02901 #ifdef IS_BIG_ENDIAN
02902 #define BLUE_BYTE 3
02903 #define GREEN_BYTE 2
02904 #define RED_BYTE 1
02905 #define ALPHA_BYTE 0
02906 #else
02907 #define BLUE_BYTE 0
02908 #define GREEN_BYTE 1
02909 #define RED_BYTE 2
02910 #define ALPHA_BYTE 3
02911 #endif
02912 
02913 // Mac surfaces are big endian.
02914 #if defined(IS_BIG_ENDIAN) || defined(XP_MACOSX)
02915 #define NATIVE_SURFACE_IS_BIG_ENDIAN
02916 #endif
02917 
02918 // OS/2 needs this painted the other way around
02919 #ifdef XP_OS2
02920 #define NATIVE_SURFACE_IS_VERTICALLY_FLIPPED
02921 #endif
02922 
02923     // Convert the data
02924     PRUint8* dest = tmpBuf;
02925     PRInt32 index = 0;
02926     
02927     PRUint32 RScale = ComputeScaleFactor(format.mRedCount);
02928     PRUint32 GScale = ComputeScaleFactor(format.mGreenCount);
02929     PRUint32 BScale = ComputeScaleFactor(format.mBlueCount);
02930     
02931     for (PRInt32 i = 0; i < aSurfaceSize.height; ++i) {
02932 #ifdef NATIVE_SURFACE_IS_VERTICALLY_FLIPPED
02933         PRUint8* src = data + (aSurfaceSize.height-1 - i)*rowSpan;
02934 #else
02935         PRUint8* src = data + i*rowSpan;
02936 #endif
02937         for (PRInt32 j = 0; j < aSurfaceSize.width; ++j) {
02938             /* v is the pixel value */
02939 #ifdef NATIVE_SURFACE_IS_BIG_ENDIAN
02940             PRUint32 v = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
02941             v >>= (32 - 8*bytesPerPix);
02942 #else
02943             PRUint32 v = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
02944 #endif
02945             // Note that because aBlackSurface is the image rendered
02946             // onto black, the channel values we get here have
02947             // effectively been premultipled by the alpha value.
02948             dest[BLUE_BYTE] = 
02949               (PRUint8)((((v & format.mBlueMask) >> format.mBlueShift)*BScale) >> 8);
02950             dest[GREEN_BYTE] =
02951               (PRUint8)((((v & format.mGreenMask) >> format.mGreenShift)*GScale) >> 8);
02952             dest[RED_BYTE] =
02953               (PRUint8)((((v & format.mRedMask) >> format.mRedShift)*RScale) >> 8);
02954             dest[ALPHA_BYTE] = alphas ? alphas[index++] : 0xFF;
02955             src += bytesPerPix;
02956             dest += 4;
02957         }
02958     }
02959 
02960 #undef RED_BYTE
02961 #undef GREEN_BYTE
02962 #undef BLUE_BYTE
02963 #undef ALPHA_BYTE
02964 
02965     cairo_set_source_surface(mCairo, tmpSurf, 0, 0);
02966     cairo_paint_with_alpha(mCairo, CurrentState().globalAlpha);
02967     
02968     cairo_surface_destroy(tmpSurf);
02969     aBlackSurface->Unlock();
02970     return Redraw();
02971 }
02972 
02973 //
02974 // device pixel getting/setting
02975 //
02976 
02977 // ImageData getImageData (in float x, in float y, in float width, in float height);
02978 NS_IMETHODIMP
02979 nsCanvasRenderingContext2D::GetImageData()
02980 {
02981     nsresult rv;
02982 
02983     if (!mCanvasElement)
02984         return NS_ERROR_FAILURE;
02985 
02986     if (mCanvasElement->IsWriteOnly()) {
02987         nsCOMPtr<nsIScriptSecurityManager> ssm =
02988             do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
02989         if (!ssm)
02990             return NS_ERROR_FAILURE;
02991 
02992         PRBool isTrusted = PR_FALSE;
02993         PRBool isChrome = PR_FALSE;
02994         PRBool hasCap = PR_FALSE;
02995 
02996         // The secman really should handle UniversalXPConnect case, since that
02997         // should include UniversalBrowserRead... doesn't right now, though.
02998         if ((NS_SUCCEEDED(ssm->SubjectPrincipalIsSystem(&isChrome)) && isChrome) ||
02999             (NS_SUCCEEDED(ssm->IsCapabilityEnabled("UniversalBrowserRead", &hasCap)) && hasCap) ||
03000             (NS_SUCCEEDED(ssm->IsCapabilityEnabled("UniversalXPConnect", &hasCap)) && hasCap))
03001         {
03002             isTrusted = PR_TRUE;
03003         }
03004 
03005         if (!isTrusted) {
03006             // not permitted to use DrawWindow
03007             // XXX ERRMSG we need to report an error to developers here! (bug 329026)
03008             return NS_ERROR_DOM_SECURITY_ERR;
03009         }
03010     }
03011 
03012     nsCOMPtr<nsIXPCNativeCallContext> ncc;
03013     rv = nsContentUtils::XPConnect()->
03014         GetCurrentNativeCallContext(getter_AddRefs(ncc));
03015     NS_ENSURE_SUCCESS(rv, rv);
03016 
03017     if (!ncc)
03018         return NS_ERROR_FAILURE;
03019 
03020     JSContext *ctx = nsnull;
03021 
03022     rv = ncc->GetJSContext(&ctx);
03023     NS_ENSURE_SUCCESS(rv, rv);
03024 
03025     PRUint32 argc;
03026     jsval *argv = nsnull;
03027 
03028     ncc->GetArgc(&argc);
03029     ncc->GetArgvPtr(&argv);
03030 
03031     int32 x, y, w, h;
03032     if (!JS_ConvertArguments (ctx, argc, argv, "jjjj", &x, &y, &w, &h))
03033         return NS_ERROR_DOM_SYNTAX_ERR;
03034 
03035     if (!CheckSaneSubrectSize (x, y, w, h, mWidth, mHeight))
03036         return NS_ERROR_DOM_SYNTAX_ERR;
03037 
03038     PRUint8 *surfaceData = mImageSurfaceData;
03039     nsAutoArrayPtr<PRUint8> allocatedSurfaceData;
03040     int surfaceDataStride = mWidth * 4;
03041     int surfaceDataOffset = (surfaceDataStride * y) + (x * 4);
03042 
03043     if (!surfaceData) {
03044         allocatedSurfaceData = new PRUint8[w * h * 4];
03045         if (!allocatedSurfaceData)
03046             return NS_ERROR_OUT_OF_MEMORY;
03047         surfaceData = allocatedSurfaceData.get();
03048 
03049         cairo_surface_t *tmpsurf = cairo_image_surface_create_for_data (surfaceData,
03050                                                                         CAIRO_FORMAT_ARGB32,
03051                                                                         w, h, w*4);
03052         cairo_t *tmpcr = cairo_create (tmpsurf);
03053         cairo_set_operator (tmpcr, CAIRO_OPERATOR_SOURCE);
03054         cairo_set_source_surface (tmpcr, mSurface, -(int)x, -(int)y);
03055         cairo_paint (tmpcr);
03056         cairo_destroy (tmpcr);
03057         cairo_surface_destroy (tmpsurf);
03058 
03059         surfaceDataStride = w * 4;
03060         surfaceDataOffset = 0;
03061     }
03062 
03063     PRUint32 len = w * h * 4;
03064     if (len > (((PRUint32)0xfff00000)/sizeof(jsval)))
03065         return NS_ERROR_INVALID_ARG;
03066 
03067     nsAutoArrayPtr<jsval> jsvector(new jsval[w * h * 4]);
03068     if (!jsvector)
03069         return NS_ERROR_OUT_OF_MEMORY;
03070     jsval *dest = jsvector.get();
03071     PRUint8 *row;
03072     for (int j = 0; j < h; j++) {
03073         row = surfaceData + surfaceDataOffset + (surfaceDataStride * j);
03074         for (int i = 0; i < w; i++) {
03075             // XXX Is there some useful swizzle MMX we can use here?
03076             // I guess we have to INT_TO_JSVAL still
03077 #ifdef IS_LITTLE_ENDIAN
03078             PRUint8 b = *row++;
03079             PRUint8 g = *row++;
03080             PRUint8 r = *row++;
03081             PRUint8 a = *row++;
03082 #else
03083             PRUint8 a = *row++;
03084             PRUint8 r = *row++;
03085             PRUint8 g = *row++;
03086             PRUint8 b = *row++;
03087 #endif
03088             *dest++ = INT_TO_JSVAL(r);
03089             *dest++ = INT_TO_JSVAL(g);
03090             *dest++ = INT_TO_JSVAL(b);
03091             *dest++ = INT_TO_JSVAL(a);
03092         }
03093     }
03094 
03095     JSObject *dataArray = JS_NewArrayObject(ctx, w*h*4, jsvector.get());
03096     if (!dataArray)
03097         return NS_ERROR_OUT_OF_MEMORY;
03098 
03099     nsAutoGCRoot arrayGCRoot(&dataArray, &rv);
03100     NS_ENSURE_SUCCESS(rv, rv);
03101 
03102     JSObject *result = JS_NewObject(ctx, NULL, NULL, NULL);
03103     if (!result)
03104         return NS_ERROR_OUT_OF_MEMORY;
03105 
03106     nsAutoGCRoot resultGCRoot(&result, &rv);
03107     NS_ENSURE_SUCCESS(rv, rv);
03108 
03109     if (!JS_DefineProperty(ctx, result, "width", INT_TO_JSVAL(w), NULL, NULL, 0) ||
03110         !JS_DefineProperty(ctx, result, "height", INT_TO_JSVAL(h), NULL, NULL, 0) ||
03111         !JS_DefineProperty(ctx, result, "data", OBJECT_TO_JSVAL(dataArray), NULL, NULL, 0))
03112         return NS_ERROR_FAILURE;
03113 
03114     jsval *retvalPtr;
03115     ncc->GetRetValPtr(&retvalPtr);
03116     *retvalPtr = OBJECT_TO_JSVAL(result);
03117     ncc->SetReturnValueWasSet(PR_TRUE);
03118 
03119     return NS_OK;
03120 }
03121 
03122 // void putImageData (in ImageData d, in float x, in float y);
03123 NS_IMETHODIMP
03124 nsCanvasRenderingContext2D::PutImageData()
03125 {
03126     nsresult rv;
03127 
03128     nsCOMPtr<nsIXPCNativeCallContext> ncc;
03129     rv = nsContentUtils::XPConnect()->
03130         GetCurrentNativeCallContext(getter_AddRefs(ncc));
03131     NS_ENSURE_SUCCESS(rv, rv);
03132 
03133     if (!ncc)
03134         return NS_ERROR_FAILURE;
03135 
03136     JSContext *ctx = nsnull;
03137 
03138     rv = ncc->GetJSContext(&ctx);
03139     NS_ENSURE_SUCCESS(rv, rv);
03140 
03141     PRUint32 argc;
03142     jsval *argv = nsnull;
03143 
03144     ncc->GetArgc(&argc);
03145     ncc->GetArgvPtr(&argv);
03146 
03147     JSObject *dataObject;
03148     int32 x, y;
03149 
03150     if (!JS_ConvertArguments (ctx, argc, argv, "ojj", &dataObject, &x, &y))
03151         return NS_ERROR_DOM_SYNTAX_ERR;
03152 
03153     if (!dataObject)
03154         return NS_ERROR_DOM_SYNTAX_ERR;
03155 
03156     int32 w, h;
03157     JSObject *dataArray;
03158     jsval v;
03159 
03160     if (!JS_GetProperty(ctx, dataObject, "width", &v) ||
03161         !JS_ValueToInt32(ctx, v, &w))
03162         return NS_ERROR_DOM_SYNTAX_ERR;
03163 
03164     if (!JS_GetProperty(ctx, dataObject, "height", &v) ||
03165         !JS_ValueToInt32(ctx, v, &h))
03166         return NS_ERROR_DOM_SYNTAX_ERR;
03167 
03168     if (!JS_GetProperty(ctx, dataObject, "data", &v) ||
03169         !JSVAL_IS_OBJECT(v))
03170         return NS_ERROR_DOM_SYNTAX_ERR;
03171     dataArray = JSVAL_TO_OBJECT(v);
03172 
03173     if (!CheckSaneSubrectSize (x, y, w, h, mWidth, mHeight))
03174         return NS_ERROR_DOM_SYNTAX_ERR;
03175 
03176     jsuint arrayLen;
03177     if (!JS_IsArrayObject(ctx, dataArray) ||
03178         !JS_GetArrayLength(ctx, dataArray, &arrayLen) ||
03179         arrayLen < (jsuint)(w * h * 4))
03180         return NS_ERROR_DOM_SYNTAX_ERR;
03181 
03182     nsAutoArrayPtr<PRUint8> imageBuffer(new PRUint8[w * h * 4]);
03183     cairo_surface_t *imgsurf;
03184     PRUint8 *imgPtr = imageBuffer.get();
03185     jsval vr, vg, vb, va;
03186     PRUint8 ir, ig, ib, ia;
03187     for (int32 j = 0; j < h; j++) {
03188         for (int32 i = 0; i < w; i++) {
03189             if (!JS_GetElement(ctx, dataArray, (j*w*4) + i*4 + 0, &vr) ||
03190                 !JS_GetElement(ctx, dataArray, (j*w*4) + i*4 + 1, &vg) ||
03191                 !JS_GetElement(ctx, dataArray, (j*w*4) + i*4 + 2, &vb) ||
03192                 !JS_GetElement(ctx, dataArray, (j*w*4) + i*4 + 3, &va))
03193                 return NS_ERROR_DOM_SYNTAX_ERR;
03194 
03195             if (JSVAL_IS_INT(vr))         ir = (PRUint8) JSVAL_TO_INT(vr);
03196             else if (JSVAL_IS_DOUBLE(vr)) ir = (PRUint8) (*JSVAL_TO_DOUBLE(vr));
03197             else return NS_ERROR_DOM_SYNTAX_ERR;
03198 
03199 
03200             if (JSVAL_IS_INT(vg))         ig = (PRUint8) JSVAL_TO_INT(vg);
03201             else if (JSVAL_IS_DOUBLE(vg)) ig = (PRUint8) (*JSVAL_TO_DOUBLE(vg));
03202             else return NS_ERROR_DOM_SYNTAX_ERR;
03203 
03204             if (JSVAL_IS_INT(vb))         ib = (PRUint8) JSVAL_TO_INT(vb);
03205             else if (JSVAL_IS_DOUBLE(vb)) ib = (PRUint8) (*JSVAL_TO_DOUBLE(vb));
03206             else return NS_ERROR_DOM_SYNTAX_ERR;
03207 
03208             if (JSVAL_IS_INT(va))         ia = (PRUint8) JSVAL_TO_INT(va);
03209             else if (JSVAL_IS_DOUBLE(va)) ia = (PRUint8) (*JSVAL_TO_DOUBLE(va));
03210             else return NS_ERROR_DOM_SYNTAX_ERR;
03211 
03212 #ifdef IS_LITTLE_ENDIAN
03213             *imgPtr++ = ib;
03214             *imgPtr++ = ig;
03215             *imgPtr++ = ir;
03216             *imgPtr++ = ia;
03217 #else
03218             *imgPtr++ = ia;
03219             *imgPtr++ = ir;
03220             *imgPtr++ = ig;
03221             *imgPtr++ = ib;
03222 #endif
03223         }
03224     }
03225 
03226     if (mImageSurfaceData) {
03227         int stride = mWidth*4;
03228         PRUint8 *dest = mImageSurfaceData + stride*y + x*4;
03229         PRUint8 *src = imageBuffer.get();
03230 
03231         for (int32 i = 0; i < h; i++) {
03232             memcpy(dest, src + (w*4)*i, w*4);
03233             dest += stride;
03234         }
03235     } else {
03236         imgsurf = cairo_image_surface_create_for_data (imageBuffer.get(),
03237                                                        CAIRO_FORMAT_ARGB32,
03238                                                        w, h, w*4);
03239         cairo_save (mCairo);
03240         cairo_identity_matrix (mCairo);
03241         cairo_translate (mCairo, x, y);
03242         cairo_new_path (mCairo);
03243         cairo_rectangle (mCairo, 0, 0, w, h);
03244         cairo_set_source_surface (mCairo, imgsurf, 0, 0);
03245         cairo_set_operator (mCairo, CAIRO_OPERATOR_SOURCE);
03246         cairo_fill (mCairo);
03247         cairo_restore (mCairo);
03248 
03249         cairo_surface_destroy (imgsurf);
03250     }
03251 
03252     return NS_OK;
03253 }