Added tag SEAMONKEY_2_12_1_RELEASE for changeset a347058c607a. CLOSED TREE a=release
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- *//* vim:set ts=4 sw=4 et tw=78: *//* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"base/basictypes.h"#include"nsIDOMXULElement.h"#include"prmem.h"#include"prenv.h"#include"nsIServiceManager.h"#include"nsMathUtils.h"#include"nsContentUtils.h"#include"nsIDOMDocument.h"#include"nsIDocument.h"#include"nsIDOMCanvasRenderingContext2D.h"#include"nsICanvasRenderingContextInternal.h"#include"nsHTMLCanvasElement.h"#include"nsSVGEffects.h"#include"nsPresContext.h"#include"nsIPresShell.h"#include"nsIVariant.h"#include"nsIInterfaceRequestorUtils.h"#include"nsIFrame.h"#include"nsDOMError.h"#include"nsIScriptError.h"#include"nsCSSParser.h"#include"mozilla/css/StyleRule.h"#include"mozilla/css/Declaration.h"#include"nsComputedDOMStyle.h"#include"nsStyleSet.h"#include"nsPrintfCString.h"#include"nsReadableUtils.h"#include"nsColor.h"#include"nsGfxCIID.h"#include"nsIScriptSecurityManager.h"#include"nsIDocShell.h"#include"nsIDOMWindow.h"#include"nsPIDOMWindow.h"#include"nsIDocShellTreeItem.h"#include"nsIDocShellTreeNode.h"#include"nsIXPConnect.h"#include"nsDisplayList.h"#include"nsTArray.h"#include"imgIEncoder.h"#include"gfxContext.h"#include"gfxASurface.h"#include"gfxImageSurface.h"#include"gfxPlatform.h"#include"gfxFont.h"#include"gfxBlur.h"#include"gfxUtils.h"#include"nsRenderingContext.h"#include"nsFrameManager.h"#include"nsFrameLoader.h"#include"nsBidi.h"#include"nsBidiPresUtils.h"#include"Layers.h"#include"CanvasUtils.h"#include"nsIMemoryReporter.h"#include"nsStyleUtil.h"#include"CanvasImageCache.h"#include<algorithm>#include"jsapi.h"#include"jsfriendapi.h"#include"mozilla/Assertions.h"#include"mozilla/CheckedInt.h"#include"mozilla/dom/ContentParent.h"#include"mozilla/dom/ImageData.h"#include"mozilla/dom/PBrowserParent.h"#include"mozilla/ipc/DocumentRendererParent.h"#include"mozilla/ipc/PDocumentRendererParent.h"// windows.h (included by chromium code) defines this, in its infinite wisdom#undef DrawTextusingnamespacemozilla;usingnamespacemozilla::CanvasUtils;usingnamespacemozilla::dom;usingnamespacemozilla::ipc;usingnamespacemozilla::layers;staticfloatkDefaultFontSize=10.0;staticNS_NAMED_LITERAL_STRING(kDefaultFontName,"sans-serif");staticNS_NAMED_LITERAL_STRING(kDefaultFontStyle,"10px sans-serif");/* Memory reporter stuff */staticnsIMemoryReporter*gCanvasMemoryReporter=nsnull;staticPRInt64gCanvasMemoryUsed=0;staticPRInt64GetCanvasMemoryUsed(){returngCanvasMemoryUsed;}// This is KIND_OTHER because it's not always clear where in memory the pixels of// a canvas are stored. Furthermore, this memory will be tracked by the// underlying surface implementations. See bug 655638 for details.NS_MEMORY_REPORTER_IMPLEMENT(CanvasMemory,"canvas-2d-pixel-bytes",KIND_OTHER,UNITS_BYTES,GetCanvasMemoryUsed,"Memory used by 2D canvases. Each canvas requires (width * height * 4) ""bytes.")staticvoidCopyContext(gfxContext*dest,gfxContext*src){dest->Multiply(src->CurrentMatrix());nsRefPtr<gfxPath>path=src->CopyPath();dest->NewPath();dest->AppendPath(path);nsRefPtr<gfxPattern>pattern=src->GetPattern();dest->SetPattern(pattern);dest->SetLineWidth(src->CurrentLineWidth());dest->SetLineCap(src->CurrentLineCap());dest->SetLineJoin(src->CurrentLineJoin());dest->SetMiterLimit(src->CurrentMiterLimit());dest->SetFillRule(src->CurrentFillRule());dest->SetAntialiasMode(src->CurrentAntialiasMode());AutoFallibleTArray<gfxFloat,10>dashes;doubledashOffset;if(src->CurrentDash(dashes,&dashOffset)){dest->SetDash(dashes.Elements(),dashes.Length(),dashOffset);}}/** ** nsCanvasGradient **/#define NS_CANVASGRADIENT_PRIVATE_IID \ { 0x491d39d8, 0x4058, 0x42bd, { 0xac, 0x76, 0x70, 0xd5, 0x62, 0x7f, 0x02, 0x10 } }classnsCanvasGradientMOZ_FINAL:publicnsIDOMCanvasGradient{public:NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASGRADIENT_PRIVATE_IID)nsCanvasGradient(gfxPattern*pat):mPattern(pat){}gfxPattern*GetPattern(){returnmPattern;}/* nsIDOMCanvasGradient */NS_IMETHODAddColorStop(floatoffset,constnsAString&colorstr){if(!FloatValidate(offset)||offset<0.0||offset>1.0)returnNS_ERROR_DOM_INDEX_SIZE_ERR;nscolorcolor;nsCSSParserparser;nsresultrv=parser.ParseColorString(nsString(colorstr),nsnull,0,&color);if(NS_FAILED(rv))returnNS_ERROR_DOM_SYNTAX_ERR;mPattern->AddColorStop(offset,gfxRGBA(color));returnNS_OK;}NS_DECL_ISUPPORTSprotected:nsRefPtr<gfxPattern>mPattern;};NS_DEFINE_STATIC_IID_ACCESSOR(nsCanvasGradient,NS_CANVASGRADIENT_PRIVATE_IID)NS_IMPL_ADDREF(nsCanvasGradient)NS_IMPL_RELEASE(nsCanvasGradient)DOMCI_DATA(CanvasGradient,nsCanvasGradient)NS_INTERFACE_MAP_BEGIN(nsCanvasGradient)NS_INTERFACE_MAP_ENTRY(nsCanvasGradient)NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasGradient)NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CanvasGradient)NS_INTERFACE_MAP_ENTRY(nsISupports)NS_INTERFACE_MAP_END/** ** nsCanvasPattern **/#define NS_CANVASPATTERN_PRIVATE_IID \ { 0xb85c6c8a, 0x0624, 0x4530, { 0xb8, 0xee, 0xff, 0xdf, 0x42, 0xe8, 0x21, 0x6d } }classnsCanvasPatternMOZ_FINAL:publicnsIDOMCanvasPattern{public:NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASPATTERN_PRIVATE_IID)nsCanvasPattern(gfxPattern*pat,nsIPrincipal*principalForSecurityCheck,boolforceWriteOnly,boolCORSUsed):mPattern(pat),mPrincipal(principalForSecurityCheck),mForceWriteOnly(forceWriteOnly),mCORSUsed(CORSUsed){}gfxPattern*GetPattern()const{returnmPattern;}nsIPrincipal*Principal()const{returnmPrincipal;}boolGetForceWriteOnly()const{returnmForceWriteOnly;}boolGetCORSUsed()const{returnmCORSUsed;}NS_DECL_ISUPPORTSprotected:nsRefPtr<gfxPattern>mPattern;nsCOMPtr<nsIPrincipal>mPrincipal;constboolmForceWriteOnly;constboolmCORSUsed;};NS_DEFINE_STATIC_IID_ACCESSOR(nsCanvasPattern,NS_CANVASPATTERN_PRIVATE_IID)NS_IMPL_ADDREF(nsCanvasPattern)NS_IMPL_RELEASE(nsCanvasPattern)DOMCI_DATA(CanvasPattern,nsCanvasPattern)NS_INTERFACE_MAP_BEGIN(nsCanvasPattern)NS_INTERFACE_MAP_ENTRY(nsCanvasPattern)NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasPattern)NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CanvasPattern)NS_INTERFACE_MAP_ENTRY(nsISupports)NS_INTERFACE_MAP_END/** ** nsTextMetrics **/#define NS_TEXTMETRICS_PRIVATE_IID \ { 0xc5b1c2f9, 0xcb4f, 0x4394, { 0xaf, 0xe0, 0xc6, 0x59, 0x33, 0x80, 0x8b, 0xf3 } }classnsTextMetrics:publicnsIDOMTextMetrics{public:nsTextMetrics(floatw):width(w){}virtual~nsTextMetrics(){}NS_DECLARE_STATIC_IID_ACCESSOR(NS_TEXTMETRICS_PRIVATE_IID)NS_IMETHODGetWidth(float*w){*w=width;returnNS_OK;}NS_DECL_ISUPPORTSprivate:floatwidth;};NS_DEFINE_STATIC_IID_ACCESSOR(nsTextMetrics,NS_TEXTMETRICS_PRIVATE_IID)NS_IMPL_ADDREF(nsTextMetrics)NS_IMPL_RELEASE(nsTextMetrics)DOMCI_DATA(TextMetrics,nsTextMetrics)NS_INTERFACE_MAP_BEGIN(nsTextMetrics)NS_INTERFACE_MAP_ENTRY(nsTextMetrics)NS_INTERFACE_MAP_ENTRY(nsIDOMTextMetrics)NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TextMetrics)NS_INTERFACE_MAP_ENTRY(nsISupports)NS_INTERFACE_MAP_ENDstructnsCanvasBidiProcessor;classCanvasRenderingContext2DUserData;/** ** nsCanvasRenderingContext2D **/classnsCanvasRenderingContext2D:publicnsIDOMCanvasRenderingContext2D,publicnsICanvasRenderingContextInternal{public:nsCanvasRenderingContext2D();virtual~nsCanvasRenderingContext2D();nsresultRedraw();// nsICanvasRenderingContextInternalNS_IMETHODSetCanvasElement(nsHTMLCanvasElement*aParentCanvas);NS_IMETHODSetDimensions(PRInt32width,PRInt32height);voidInitialize(nsIDocShell*shell,PRInt32width,PRInt32height);NS_IMETHODInitializeWithSurface(nsIDocShell*shell,gfxASurface*surface,PRInt32width,PRInt32height);boolEnsureSurface();NS_IMETHODRender(gfxContext*ctx,gfxPattern::GraphicsFilteraFilter,PRUint32aFlags=RenderFlagPremultAlpha);NS_IMETHODGetInputStream(constchar*aMimeType,constPRUnichar*aEncoderOptions,nsIInputStream**aStream);NS_IMETHODGetThebesSurface(gfxASurface**surface);mozilla::TemporaryRef<mozilla::gfx::SourceSurface>GetSurfaceSnapshot(){returnnsnull;}NS_IMETHODSetIsOpaque(boolisOpaque);NS_IMETHODReset();virtualalready_AddRefed<CanvasLayer>GetCanvasLayer(nsDisplayListBuilder*aBuilder,CanvasLayer*aOldLayer,LayerManager*aManager);virtualboolShouldForceInactiveLayer(LayerManager*aManager);virtualvoidMarkContextClean();NS_IMETHODSetIsIPC(boolisIPC);// this rect is in canvas device spaceNS_IMETHODRedraw(constgfxRect&r);// this rect is in mThebes's current user spaceNS_IMETHODRedrawUser(constgfxRect&r);// nsISupports interface + CCNS_DECL_CYCLE_COLLECTING_ISUPPORTSNS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsCanvasRenderingContext2D,nsIDOMCanvasRenderingContext2D)// nsIDOMCanvasRenderingContext2D interfaceNS_DECL_NSIDOMCANVASRENDERINGCONTEXT2DenumStyle{STYLE_STROKE=0,STYLE_FILL,STYLE_SHADOW,STYLE_MAX};classPathAutoSaveRestore{public:PathAutoSaveRestore(nsCanvasRenderingContext2D*aCtx):mContext(aCtx->mThebes){if(aCtx->mHasPath){mPath=mContext->CopyPath();}}~PathAutoSaveRestore(){mContext->NewPath();if(mPath){mContext->AppendPath(mPath);}}private:gfxContext*mContext;nsRefPtr<gfxPath>mPath;};friendclassPathAutoSaveRestore;friendclassCanvasRenderingContext2DUserData;protected:nsresultGetImageDataArray(JSContext*aCx,int32_taX,int32_taY,uint32_taWidth,uint32_taHeight,JSObject**aRetval);/** * The number of living nsCanvasRenderingContexts. When this goes down to * 0, we free the premultiply and unpremultiply tables, if they exist. */staticPRUint32sNumLivingContexts;/** * Lookup table used to speed up GetImageData(). */staticPRUint8(*sUnpremultiplyTable)[256];/** * Lookup table used to speed up PutImageData(). */staticPRUint8(*sPremultiplyTable)[256];// Some helpers. Doesn't modify acolor on failure.nsresultSetStyleFromStringOrInterface(constnsAString&aStr,nsISupports*aInterface,StyleaWhichStyle);nsresultGetStyleAsStringOrInterface(nsAString&aStr,nsISupports**aInterface,PRInt32*aType,StyleaWhichStyle);voidStyleColorToString(constnscolor&aColor,nsAString&aStr);voidDirtyAllStyles();/** * applies the given style as the current source. If the given style is * a solid color, aUseGlobalAlpha indicates whether to multiply the alpha * by global alpha, and is ignored otherwise. */voidApplyStyle(StyleaWhichStyle,boolaUseGlobalAlpha=true);/** * Creates the unpremultiply lookup table, if it doesn't exist. */voidEnsureUnpremultiplyTable();/** * Creates the premultiply lookup table, if it doesn't exist. */voidEnsurePremultiplyTable();/** * Returns the image format this canvas should be allocated using. Takes * into account mOpaque, platform requirements, etc. */gfxASurface::gfxImageFormatGetImageFormat()const;// Member varsPRInt32mWidth,mHeight;boolmValid;boolmZero;boolmOpaque;boolmResetLayer;boolmIPC;// the canvas element we're a context ofnsCOMPtr<nsIDOMHTMLCanvasElement>mCanvasElement;nsHTMLCanvasElement*HTMLCanvasElement(){returnstatic_cast<nsHTMLCanvasElement*>(mCanvasElement.get());}// Initialize the Thebes rendering contextvoidCreateThebes();// If mCanvasElement is not provided, then a docshell isnsCOMPtr<nsIDocShell>mDocShell;nsTArray<CanvasRenderingContext2DUserData*>mUserDatas;// our drawing surfaces, contexts, and layersnsRefPtr<gfxContext>mThebes;nsRefPtr<gfxASurface>mSurface;boolmSurfaceCreated;PRUint32mSaveCount;/** * Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever * Redraw is called, reset to false when Render is called. */boolmIsEntireFrameInvalid;/** * When this is set, the first call to Redraw(gfxRect) should set * mIsEntireFrameInvalid since we expect it will be followed by * many more Redraw calls. */boolmPredictManyRedrawCalls;/** * This is set whenever there's a nonempty path set by the API user. */boolmHasPath;/** * Number of times we've invalidated before calling redraw */PRUint32mInvalidateCount;staticconstPRUint32kCanvasMaxInvalidateCount=100;/** * Returns true iff the the given operator should affect areas of the * destination where the source is transparent. Among other things, this * implies that a fully transparent source would still affect the canvas. */boolOperatorAffectsUncoveredAreas(gfxContext::GraphicsOperatorop)const{returnop==gfxContext::OPERATOR_IN||op==gfxContext::OPERATOR_OUT||op==gfxContext::OPERATOR_DEST_IN||op==gfxContext::OPERATOR_DEST_ATOP;}/** * Returns true iff a shadow should be drawn along with a * drawing operation. */boolNeedToDrawShadow(){ContextState&state=CurrentState();// The spec says we should not draw shadows when the alpha value is 0,// regardless of the operator being used.returnstate.StyleIsColor(STYLE_SHADOW)&&NS_GET_A(state.colorStyles[STYLE_SHADOW])>0&&(state.shadowOffset!=gfxPoint(0,0)||state.shadowBlur!=0);}/** * Checks the current state to determine if an intermediate surface would * be necessary to complete a drawing operation. Does not check the * condition pertaining to global alpha and patterns since that does not * pertain to all drawing operations. */boolNeedToUseIntermediateSurface(){if(!mThebes){// Haven't created a surface yet, default is OVER.returnOperatorAffectsUncoveredAreas(gfxContext::OPERATOR_OVER);}// certain operators always need an intermediate surface, except// with quartz since quartz does compositing differently than cairoreturnOperatorAffectsUncoveredAreas(mThebes->CurrentOperator());// XXX there are other unhandled cases but they should be investigated// first to ensure we aren't using an intermediate surface unecessarily}/** * If the current operator is "source" then clear the destination before we * draw into it, to simulate the effect of an unbounded source operator. */voidClearSurfaceForUnboundedSource(){if(!mThebes){// Haven't created a surface yet, default is OVER.return;}gfxContext::GraphicsOperatorcurrent=mThebes->CurrentOperator();if(current!=gfxContext::OPERATOR_SOURCE)return;mThebes->SetOperator(gfxContext::OPERATOR_CLEAR);// It doesn't really matter what the source is here, since Paint// isn't bounded by the source and the mask covers the entire clip// region.mThebes->Paint();mThebes->SetOperator(current);}/** * Returns true iff the current source is such that global alpha would not * be handled correctly without the use of an intermediate surface. */boolNeedIntermediateSurfaceToHandleGlobalAlpha(StyleaWhichStyle){returnCurrentState().globalAlpha!=1.0&&!CurrentState().StyleIsColor(aWhichStyle);}/** * Initializes the drawing of a shadow onto the canvas. The returned context * should have the shadow shape drawn onto it, and then ShadowFinalize * should be called. The return value is null if an error occurs. * @param extents The extents of the shadow object, in device space. * @param blur A newly contructed gfxAlphaBoxBlur, made with the default * constructor and left uninitialized. * @remark The lifetime of the return value is tied to the lifetime of * the gfxAlphaBoxBlur, so it does not need to be ref counted. */gfxContext*ShadowInitialize(constgfxRect&extents,gfxAlphaBoxBlur&blur);/** * Completes a shadow drawing operation. * @param blur The gfxAlphaBoxBlur that was passed to ShadowInitialize. */voidShadowFinalize(gfxAlphaBoxBlur&blur);/** * Draws the current path in the given style. Takes care of * any shadow drawing and will use intermediate surfaces as needed. * * If dirtyRect is given, it will contain the user-space dirty * rectangle of the draw operation. */nsresultDrawPath(Stylestyle,gfxRect*dirtyRect=nsnull);/** * Draws a rectangle in the given style; used by FillRect and StrokeRect. */nsresultDrawRect(constgfxRect&rect,Stylestyle);/** * Gets the pres shell from either the canvas element or the doc shell */nsIPresShell*GetPresShell(){nsCOMPtr<nsIContent>content=do_QueryObject(mCanvasElement);if(content){returncontent->OwnerDoc()->GetShell();}if(mDocShell){nsCOMPtr<nsIPresShell>shell;mDocShell->GetPresShell(getter_AddRefs(shell));returnshell.get();}returnnsnull;}// textenumTextAlign{TEXT_ALIGN_START,TEXT_ALIGN_END,TEXT_ALIGN_LEFT,TEXT_ALIGN_RIGHT,TEXT_ALIGN_CENTER};enumTextBaseline{TEXT_BASELINE_TOP,TEXT_BASELINE_HANGING,TEXT_BASELINE_MIDDLE,TEXT_BASELINE_ALPHABETIC,TEXT_BASELINE_IDEOGRAPHIC,TEXT_BASELINE_BOTTOM};gfxFontGroup*GetCurrentFontStyle();gfxTextRun*MakeTextRun(constPRUnichar*aText,PRUint32aLength,PRUint32aAppUnitsPerDevUnit,PRUint32aFlags);enumTextDrawOperation{TEXT_DRAW_OPERATION_FILL,TEXT_DRAW_OPERATION_STROKE,TEXT_DRAW_OPERATION_MEASURE};/* * Implementation of the fillText, strokeText, and measure functions with * the operation abstracted to a flag. */nsresultDrawOrMeasureText(constnsAString&text,floatx,floaty,floatmaxWidth,TextDrawOperationop,float*aWidth);// style handling/* * The previous set style. Is equal to STYLE_MAX when there is no valid * previous style. */StylemLastStyle;boolmDirtyStyle[STYLE_MAX];// state stack handlingclassContextState{public:ContextState():shadowOffset(0.0,0.0),globalAlpha(1.0),shadowBlur(0.0),textAlign(TEXT_ALIGN_START),textBaseline(TEXT_BASELINE_ALPHABETIC),imageSmoothingEnabled(true){}ContextState(constContextState&other):shadowOffset(other.shadowOffset),globalAlpha(other.globalAlpha),shadowBlur(other.shadowBlur),font(other.font),fontGroup(other.fontGroup),textAlign(other.textAlign),textBaseline(other.textBaseline),imageSmoothingEnabled(other.imageSmoothingEnabled){for(inti=0;i<STYLE_MAX;i++){colorStyles[i]=other.colorStyles[i];gradientStyles[i]=other.gradientStyles[i];patternStyles[i]=other.patternStyles[i];}}inlinevoidSetColorStyle(StylewhichStyle,nscolorcolor){colorStyles[whichStyle]=color;gradientStyles[whichStyle]=nsnull;patternStyles[whichStyle]=nsnull;}inlinevoidSetPatternStyle(StylewhichStyle,nsCanvasPattern*pat){gradientStyles[whichStyle]=nsnull;patternStyles[whichStyle]=pat;}inlinevoidSetGradientStyle(StylewhichStyle,nsCanvasGradient*grad){gradientStyles[whichStyle]=grad;patternStyles[whichStyle]=nsnull;}/** * returns true iff the given style is a solid color. */inlineboolStyleIsColor(StylewhichStyle)const{return!(patternStyles[whichStyle]||gradientStyles[whichStyle]);}gfxPointshadowOffset;floatglobalAlpha;floatshadowBlur;nsStringfont;nsRefPtr<gfxFontGroup>fontGroup;TextAligntextAlign;TextBaselinetextBaseline;nscolorcolorStyles[STYLE_MAX];nsCOMPtr<nsCanvasGradient>gradientStyles[STYLE_MAX];nsCOMPtr<nsCanvasPattern>patternStyles[STYLE_MAX];boolimageSmoothingEnabled;};nsTArray<ContextState>mStyleStack;inlineContextState&CurrentState(){returnmStyleStack[mSaveCount];}// other helpersvoidGetAppUnitsValues(PRUint32*perDevPixel,PRUint32*perCSSPixel){// If we don't have a canvas element, we just return something generic.PRUint32devPixel=60;PRUint32cssPixel=60;nsIPresShell*ps=GetPresShell();nsPresContext*pc;if(!ps)gotoFINISH;pc=ps->GetPresContext();if(!pc)gotoFINISH;devPixel=pc->AppUnitsPerDevPixel();cssPixel=pc->AppUnitsPerCSSPixel();FINISH:if(perDevPixel)*perDevPixel=devPixel;if(perCSSPixel)*perCSSPixel=cssPixel;}friendstructnsCanvasBidiProcessor;};classCanvasRenderingContext2DUserData:publicLayerUserData{public:CanvasRenderingContext2DUserData(nsCanvasRenderingContext2D*aContext):mContext(aContext){aContext->mUserDatas.AppendElement(this);}~CanvasRenderingContext2DUserData(){if(mContext){mContext->mUserDatas.RemoveElement(this);}}staticvoidDidTransactionCallback(void*aData){CanvasRenderingContext2DUserData*self=static_cast<CanvasRenderingContext2DUserData*>(aData);if(self->mContext){self->mContext->MarkContextClean();}}boolIsForContext(nsCanvasRenderingContext2D*aContext){returnmContext==aContext;}voidForget(){mContext=nsnull;}private:nsCanvasRenderingContext2D*mContext;};NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCanvasRenderingContext2D)NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCanvasRenderingContext2D)NS_IMPL_CYCLE_COLLECTION_CLASS(nsCanvasRenderingContext2D)NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsCanvasRenderingContext2D)NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCanvasElement)NS_IMPL_CYCLE_COLLECTION_UNLINK_ENDNS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsCanvasRenderingContext2D)NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCanvasElement)NS_IMPL_CYCLE_COLLECTION_TRAVERSE_ENDDOMCI_DATA(CanvasRenderingContext2D,nsCanvasRenderingContext2D)NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCanvasRenderingContext2D)NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasRenderingContext2D)NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports,nsIDOMCanvasRenderingContext2D)NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CanvasRenderingContext2D)NS_INTERFACE_MAP_END/** ** CanvasRenderingContext2D impl **/// Initialize our static variables.PRUint32nsCanvasRenderingContext2D::sNumLivingContexts=0;PRUint8(*nsCanvasRenderingContext2D::sUnpremultiplyTable)[256]=nsnull;PRUint8(*nsCanvasRenderingContext2D::sPremultiplyTable)[256]=nsnull;nsresultNS_NewCanvasRenderingContext2DThebes(nsIDOMCanvasRenderingContext2D**aResult){nsRefPtr<nsIDOMCanvasRenderingContext2D>ctx=newnsCanvasRenderingContext2D();if(!ctx)returnNS_ERROR_OUT_OF_MEMORY;*aResult=ctx.forget().get();returnNS_OK;}nsCanvasRenderingContext2D::nsCanvasRenderingContext2D():mValid(false),mZero(false),mOpaque(false),mResetLayer(true),mIPC(false),mCanvasElement(nsnull),mSaveCount(0),mIsEntireFrameInvalid(false),mPredictManyRedrawCalls(false),mHasPath(false),mInvalidateCount(0),mLastStyle(STYLE_MAX),mStyleStack(20){sNumLivingContexts++;}nsCanvasRenderingContext2D::~nsCanvasRenderingContext2D(){Reset();// Drop references from all CanvasRenderingContext2DUserDatas to this contextfor(PRUint32i=0;i<mUserDatas.Length();++i){mUserDatas[i]->Forget();}sNumLivingContexts--;if(!sNumLivingContexts){delete[]sUnpremultiplyTable;delete[]sPremultiplyTable;sUnpremultiplyTable=nsnull;sPremultiplyTable=nsnull;}}nsresultnsCanvasRenderingContext2D::Reset(){if(mCanvasElement){HTMLCanvasElement()->InvalidateCanvas();}// only do this for non-docshell created contexts,// since those are the ones that we created a surface forif(mValid&&!mDocShell&&mSurface)gCanvasMemoryUsed-=mWidth*mHeight*4;mSurface=nsnull;mThebes=nsnull;mValid=false;mIsEntireFrameInvalid=false;mPredictManyRedrawCalls=false;returnNS_OK;}nsresultnsCanvasRenderingContext2D::SetStyleFromStringOrInterface(constnsAString&aStr,nsISupports*aInterface,StyleaWhichStyle){nsresultrv;nscolorcolor;if(!aStr.IsVoid()){nsIDocument*document=mCanvasElement?HTMLCanvasElement()->OwnerDoc():nsnull;// Pass the CSS Loader object to the parser, to allow parser error// reports to include the outer window ID.nsCSSParserparser(document?document->CSSLoader():nsnull);rv=parser.ParseColorString(aStr,nsnull,0,&color);if(NS_FAILED(rv)){// Error reporting happens inside the CSS parserreturnNS_OK;}CurrentState().SetColorStyle(aWhichStyle,color);mDirtyStyle[aWhichStyle]=true;returnNS_OK;}if(aInterface){nsCOMPtr<nsCanvasGradient>grad(do_QueryInterface(aInterface));if(grad){CurrentState().SetGradientStyle(aWhichStyle,grad);mDirtyStyle[aWhichStyle]=true;returnNS_OK;}nsCOMPtr<nsCanvasPattern>pattern(do_QueryInterface(aInterface));if(pattern){CurrentState().SetPatternStyle(aWhichStyle,pattern);mDirtyStyle[aWhichStyle]=true;returnNS_OK;}}nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,"Canvas",mCanvasElement?HTMLCanvasElement()->OwnerDoc():nsnull,nsContentUtils::eDOM_PROPERTIES,"UnexpectedCanvasVariantStyle");returnNS_OK;}nsresultnsCanvasRenderingContext2D::GetStyleAsStringOrInterface(nsAString&aStr,nsISupports**aInterface,PRInt32*aType,StyleaWhichStyle){if(CurrentState().patternStyles[aWhichStyle]){aStr.SetIsVoid(true);NS_ADDREF(*aInterface=CurrentState().patternStyles[aWhichStyle]);*aType=CMG_STYLE_PATTERN;}elseif(CurrentState().gradientStyles[aWhichStyle]){aStr.SetIsVoid(true);NS_ADDREF(*aInterface=CurrentState().gradientStyles[aWhichStyle]);*aType=CMG_STYLE_GRADIENT;}else{StyleColorToString(CurrentState().colorStyles[aWhichStyle],aStr);*aInterface=nsnull;*aType=CMG_STYLE_STRING;}returnNS_OK;}voidnsCanvasRenderingContext2D::StyleColorToString(constnscolor&aColor,nsAString&aStr){// We can't reuse the normal CSS color stringification code,// because the spec calls for a different algorithm for canvas.if(NS_GET_A(aColor)==255){CopyUTF8toUTF16(nsPrintfCString("#%02x%02x%02x",NS_GET_R(aColor),NS_GET_G(aColor),NS_GET_B(aColor)),aStr);}else{CopyUTF8toUTF16(nsPrintfCString("rgba(%d, %d, %d, ",NS_GET_R(aColor),NS_GET_G(aColor),NS_GET_B(aColor)),aStr);aStr.AppendFloat(nsStyleUtil::ColorComponentToFloat(NS_GET_A(aColor)));aStr.Append(')');}}voidnsCanvasRenderingContext2D::DirtyAllStyles(){for(inti=0;i<STYLE_MAX;i++){mDirtyStyle[i]=true;}}voidnsCanvasRenderingContext2D::ApplyStyle(StyleaWhichStyle,boolaUseGlobalAlpha){if(mLastStyle==aWhichStyle&&!mDirtyStyle[aWhichStyle]&&aUseGlobalAlpha){// nothing to do, this is already the set stylereturn;}if(!EnsureSurface()){return;}// if not using global alpha, don't optimize with dirty bitif(aUseGlobalAlpha)mDirtyStyle[aWhichStyle]=false;mLastStyle=aWhichStyle;nsCanvasPattern*pattern=CurrentState().patternStyles[aWhichStyle];if(pattern){if(mCanvasElement)CanvasUtils::DoDrawImageSecurityCheck(HTMLCanvasElement(),pattern->Principal(),pattern->GetForceWriteOnly(),pattern->GetCORSUsed());gfxPattern*gpat=pattern->GetPattern();if(CurrentState().imageSmoothingEnabled)gpat->SetFilter(gfxPattern::FILTER_GOOD);elsegpat->SetFilter(gfxPattern::FILTER_NEAREST);mThebes->SetPattern(gpat);return;}if(CurrentState().gradientStyles[aWhichStyle]){gfxPattern*gpat=CurrentState().gradientStyles[aWhichStyle]->GetPattern();mThebes->SetPattern(gpat);return;}gfxRGBAcolor(CurrentState().colorStyles[aWhichStyle]);if(aUseGlobalAlpha)color.a*=CurrentState().globalAlpha;mThebes->SetColor(color);}nsresultnsCanvasRenderingContext2D::Redraw(){if(mIsEntireFrameInvalid)returnNS_OK;mIsEntireFrameInvalid=true;if(!mCanvasElement){NS_ASSERTION(mDocShell,"Redraw with no canvas element or docshell!");returnNS_OK;}nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement());HTMLCanvasElement()->InvalidateCanvasContent(nsnull);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::Redraw(constgfxRect&r){++mInvalidateCount;if(mIsEntireFrameInvalid)returnNS_OK;if(mPredictManyRedrawCalls||mInvalidateCount>kCanvasMaxInvalidateCount){returnRedraw();}if(!mCanvasElement){NS_ASSERTION(mDocShell,"Redraw with no canvas element or docshell!");returnNS_OK;}nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement());HTMLCanvasElement()->InvalidateCanvasContent(&r);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::RedrawUser(constgfxRect&r){if(mIsEntireFrameInvalid){++mInvalidateCount;returnNS_OK;}returnRedraw(mThebes->UserToDevice(r));}NS_IMETHODIMPnsCanvasRenderingContext2D::SetDimensions(PRInt32width,PRInt32height){Initialize(NULL,width,height);returnNS_OK;}voidnsCanvasRenderingContext2D::Initialize(nsIDocShell*docShell,PRInt32width,PRInt32height){Reset();NS_ASSERTION(!docShell^!mCanvasElement,"Cannot set both docshell and canvas element");mDocShell=docShell;mWidth=width;mHeight=height;mResetLayer=true;mValid=true;mSurfaceCreated=false;// set up the initial canvas defaultsmStyleStack.Clear();mSaveCount=0;ContextState*state=mStyleStack.AppendElement();state->globalAlpha=1.0;state->colorStyles[STYLE_FILL]=NS_RGB(0,0,0);state->colorStyles[STYLE_STROKE]=NS_RGB(0,0,0);state->colorStyles[STYLE_SHADOW]=NS_RGBA(0,0,0,0);DirtyAllStyles();// always force a redraw, because if the surface dimensions were reset// then the surface became cleared, and we need to redraw everything.Redraw();return;}voidnsCanvasRenderingContext2D::CreateThebes(){mThebes=newgfxContext(mSurface);mSurfaceCreated=true;mThebes->SetOperator(gfxContext::OPERATOR_CLEAR);mThebes->NewPath();mThebes->Rectangle(gfxRect(0,0,mWidth,mHeight));mThebes->Fill();mThebes->SetLineWidth(1.0);mThebes->SetOperator(gfxContext::OPERATOR_OVER);mThebes->SetMiterLimit(10.0);mThebes->SetLineCap(gfxContext::LINE_CAP_BUTT);mThebes->SetLineJoin(gfxContext::LINE_JOIN_MITER);mThebes->NewPath();}NS_IMETHODIMPnsCanvasRenderingContext2D::InitializeWithSurface(nsIDocShell*docShell,gfxASurface*surface,PRInt32width,PRInt32height){Initialize(docShell,width,height);mSurface=surface;CreateThebes();returnmValid?NS_OK:NS_ERROR_OUT_OF_MEMORY;}boolnsCanvasRenderingContext2D::EnsureSurface(){if(!mValid){returnfalse;}if(mSurface&&mThebes&&mSurfaceCreated){if(mSurface->CairoStatus()){returnfalse;}returntrue;}nsRefPtr<gfxASurface>surface;// Check that the dimensions are saneif(gfxASurface::CheckSurfaceSize(gfxIntSize(mWidth,mHeight),0xffff)){// Zero sized surfaces have problems, so just use a 1 by 1.if(mHeight==0||mWidth==0){mZero=true;mHeight=1;mWidth=1;}else{mZero=false;}gfxASurface::gfxImageFormatformat=GetImageFormat();if(!PR_GetEnv("MOZ_CANVAS_IMAGE_SURFACE")){nsCOMPtr<nsIContent>content=do_QueryObject(mCanvasElement);nsIDocument*ownerDoc=nsnull;if(content)ownerDoc=content->OwnerDoc();nsRefPtr<LayerManager>layerManager=nsnull;if(ownerDoc)layerManager=nsContentUtils::PersistentLayerManagerForDocument(ownerDoc);if(layerManager){surface=layerManager->CreateOptimalSurface(gfxIntSize(mWidth,mHeight),format);}else{surface=gfxPlatform::GetPlatform()->CreateOffscreenSurface(gfxIntSize(mWidth,mHeight),gfxASurface::ContentFromFormat(format));}}if(!surface||surface->CairoStatus()){// If we couldn't create a surface of the type we want, fall back// to an image surface. This lets us handle surface sizes that// the underlying cairo backend might not handle.surface=newgfxImageSurface(gfxIntSize(mWidth,mHeight),format);if(!surface||surface->CairoStatus()){surface=nsnull;}}}if(surface){if(gCanvasMemoryReporter==nsnull){gCanvasMemoryReporter=newNS_MEMORY_REPORTER_NAME(CanvasMemory);NS_RegisterMemoryReporter(gCanvasMemoryReporter);}gCanvasMemoryUsed+=mWidth*mHeight*4;JSContext*context=nsContentUtils::GetCurrentJSContext();if(context){JS_updateMallocCounter(context,mWidth*mHeight*4);}}else{returnfalse;}mSurface=surface;CreateThebes();if(mSurface->CairoStatus()){returnfalse;}returntrue;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetIsOpaque(boolisOpaque){if(isOpaque==mOpaque)returnNS_OK;mOpaque=isOpaque;if(mValid){/* If we've already been created, let SetDimensions take care of * recreating our surface */returnSetDimensions(mWidth,mHeight);}returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetIsIPC(boolisIPC){if(isIPC==mIPC)returnNS_OK;mIPC=isIPC;if(mValid){/* If we've already been created, let SetDimensions take care of * recreating our surface */returnSetDimensions(mWidth,mHeight);}returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::Render(gfxContext*ctx,gfxPattern::GraphicsFilteraFilter,PRUint32aFlags){nsresultrv=NS_OK;if(!EnsureSurface())returnNS_ERROR_FAILURE;nsRefPtr<gfxPattern>pat=newgfxPattern(mSurface);pat->SetFilter(aFilter);pat->SetExtend(gfxPattern::EXTEND_PAD);gfxContext::GraphicsOperatorop=ctx->CurrentOperator();if(mOpaque)ctx->SetOperator(gfxContext::OPERATOR_SOURCE);// XXX I don't want to use PixelSnapped here, but layout doesn't guarantee// pixel alignment for this stuff!ctx->NewPath();ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0,0,mWidth,mHeight),pat);ctx->Fill();if(mOpaque)ctx->SetOperator(op);if(!(aFlags&RenderFlagPremultAlpha)){nsRefPtr<gfxASurface>curSurface=ctx->CurrentSurface();nsRefPtr<gfxImageSurface>gis=curSurface->GetAsImageSurface();NS_ABORT_IF_FALSE(gis,"If non-premult alpha, must be able to get image surface!");gfxUtils::UnpremultiplyImageSurface(gis);}returnrv;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetInputStream(constchar*aMimeType,constPRUnichar*aEncoderOptions,nsIInputStream**aStream){if(!EnsureSurface())returnNS_ERROR_FAILURE;nsresultrv;constcharencoderPrefix[]="@mozilla.org/image/encoder;2?type=";nsAutoArrayPtr<char>conid(new(std::nothrow)char[strlen(encoderPrefix)+strlen(aMimeType)+1]);if(!conid)returnNS_ERROR_OUT_OF_MEMORY;strcpy(conid,encoderPrefix);strcat(conid,aMimeType);nsCOMPtr<imgIEncoder>encoder=do_CreateInstance(conid);if(!encoder)returnNS_ERROR_FAILURE;nsAutoArrayPtr<PRUint8>imageBuffer(new(std::nothrow)PRUint8[mWidth*mHeight*4]);if(!imageBuffer)returnNS_ERROR_OUT_OF_MEMORY;nsRefPtr<gfxImageSurface>imgsurf=newgfxImageSurface(imageBuffer.get(),gfxIntSize(mWidth,mHeight),mWidth*4,gfxASurface::ImageFormatARGB32);if(!imgsurf||imgsurf->CairoStatus())returnNS_ERROR_FAILURE;nsRefPtr<gfxContext>ctx=newgfxContext(imgsurf);if(!ctx||ctx->HasError())returnNS_ERROR_FAILURE;ctx->SetOperator(gfxContext::OPERATOR_SOURCE);ctx->SetSource(mSurface,gfxPoint(0,0));ctx->Paint();rv=encoder->InitFromData(imageBuffer.get(),mWidth*mHeight*4,mWidth,mHeight,mWidth*4,imgIEncoder::INPUT_FORMAT_HOSTARGB,nsDependentString(aEncoderOptions));NS_ENSURE_SUCCESS(rv,rv);returnCallQueryInterface(encoder,aStream);}gfxASurface::gfxImageFormatnsCanvasRenderingContext2D::GetImageFormat()const{gfxASurface::gfxImageFormatformat=gfxASurface::ImageFormatARGB32;if(mOpaque)format=gfxASurface::ImageFormatRGB24;returnformat;}//// nsCanvasRenderingContext2D impl//NS_IMETHODIMPnsCanvasRenderingContext2D::SetCanvasElement(nsHTMLCanvasElement*aCanvasElement){mCanvasElement=aCanvasElement;returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetCanvas(nsIDOMHTMLCanvasElement**canvas){NS_IF_ADDREF(*canvas=mCanvasElement);returnNS_OK;}//// state//NS_IMETHODIMPnsCanvasRenderingContext2D::Save(){if(!EnsureSurface())returnNS_ERROR_FAILURE;ContextStatestate=CurrentState();mStyleStack.AppendElement(state);mThebes->Save();mSaveCount++;returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::Restore(){if(!EnsureSurface())returnNS_ERROR_FAILURE;if(mSaveCount==0)returnNS_OK;mStyleStack.RemoveElementAt(mSaveCount);mThebes->Restore();mLastStyle=STYLE_MAX;DirtyAllStyles();mSaveCount--;returnNS_OK;}//// transformations//NS_IMETHODIMPnsCanvasRenderingContext2D::Scale(floatx,floaty){if(!EnsureSurface())returnNS_ERROR_FAILURE;if(!FloatValidate(x,y))returnNS_OK;mThebes->Scale(x,y);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::Rotate(floatangle){if(!EnsureSurface())returnNS_ERROR_FAILURE;if(!FloatValidate(angle))returnNS_OK;mThebes->Rotate(angle);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::Translate(floatx,floaty){if(!EnsureSurface())returnNS_ERROR_FAILURE;if(!FloatValidate(x,y))returnNS_OK;mThebes->Translate(gfxPoint(x,y));returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::Transform(floatm11,floatm12,floatm21,floatm22,floatdx,floatdy){if(!EnsureSurface())returnNS_ERROR_FAILURE;if(!FloatValidate(m11,m12,m21,m22,dx,dy))returnNS_OK;gfxMatrixmatrix(m11,m12,m21,m22,dx,dy);mThebes->Multiply(matrix);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetTransform(floatm11,floatm12,floatm21,floatm22,floatdx,floatdy){if(!EnsureSurface())returnNS_ERROR_FAILURE;if(!FloatValidate(m11,m12,m21,m22,dx,dy))returnNS_OK;gfxMatrixmatrix(m11,m12,m21,m22,dx,dy);mThebes->SetMatrix(matrix);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetMozCurrentTransform(JSContext*cx,constjsval&matrix){nsresultrv;gfxMatrixnewCTM;if(!EnsureSurface())returnNS_ERROR_FAILURE;if(!JSValToMatrix(cx,matrix,&newCTM,&rv)){returnrv;}mThebes->SetMatrix(newCTM);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetMozCurrentTransform(JSContext*cx,jsval*matrix){if(!EnsureSurface())returnNS_ERROR_FAILURE;returnMatrixToJSVal(mThebes->CurrentMatrix(),cx,matrix);}NS_IMETHODIMPnsCanvasRenderingContext2D::SetMozCurrentTransformInverse(JSContext*cx,constjsval&matrix){nsresultrv;gfxMatrixnewCTMInverse;if(!EnsureSurface())returnNS_ERROR_FAILURE;if(!JSValToMatrix(cx,matrix,&newCTMInverse,&rv)){returnrv;}// XXX ERRMSG we need to report an error to developers here! (bug 329026)if(!newCTMInverse.IsSingular()){mThebes->SetMatrix(newCTMInverse.Invert());}returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetMozCurrentTransformInverse(JSContext*cx,jsval*matrix){gfxMatrixctm=mThebes->CurrentMatrix();if(!mThebes->CurrentMatrix().IsSingular()){ctm.Invert();}else{doubleNaN=JSVAL_TO_DOUBLE(JS_GetNaNValue(cx));ctm=gfxMatrix(NaN,NaN,NaN,NaN,NaN,NaN);}returnMatrixToJSVal(ctm,cx,matrix);}//// colors//NS_IMETHODIMPnsCanvasRenderingContext2D::SetGlobalAlpha(floataGlobalAlpha){if(!FloatValidate(aGlobalAlpha)||aGlobalAlpha<0.0||aGlobalAlpha>1.0)returnNS_OK;CurrentState().globalAlpha=aGlobalAlpha;DirtyAllStyles();returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetGlobalAlpha(float*aGlobalAlpha){*aGlobalAlpha=CurrentState().globalAlpha;returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetStrokeStyle(nsIVariant*aValue){if(!aValue)returnNS_ERROR_FAILURE;nsStringstr;nsresultrv;PRUint16vtype;rv=aValue->GetDataType(&vtype);NS_ENSURE_SUCCESS(rv,rv);if(vtype==nsIDataType::VTYPE_INTERFACE||vtype==nsIDataType::VTYPE_INTERFACE_IS){nsIID*iid;nsCOMPtr<nsISupports>sup;rv=aValue->GetAsInterface(&iid,getter_AddRefs(sup));NS_ENSURE_SUCCESS(rv,rv);if(iid)NS_Free(iid);str.SetIsVoid(true);returnSetStrokeStyle_multi(str,sup);}rv=aValue->GetAsAString(str);NS_ENSURE_SUCCESS(rv,rv);returnSetStrokeStyle_multi(str,nsnull);}NS_IMETHODIMPnsCanvasRenderingContext2D::GetStrokeStyle(nsIVariant**aResult){nsCOMPtr<nsIWritableVariant>wv=do_CreateInstance(NS_VARIANT_CONTRACTID);nsCOMPtr<nsISupports>sup;nsStringstr;PRInt32t;nsresultrv=GetStrokeStyle_multi(str,getter_AddRefs(sup),&t);NS_ENSURE_SUCCESS(rv,rv);if(t==CMG_STYLE_STRING){rv=wv->SetAsAString(str);}elseif(t==CMG_STYLE_PATTERN){rv=wv->SetAsInterface(NS_GET_IID(nsIDOMCanvasPattern),sup);}elseif(t==CMG_STYLE_GRADIENT){rv=wv->SetAsInterface(NS_GET_IID(nsIDOMCanvasGradient),sup);}else{NS_ERROR("Unknown type from GetStroke/FillStyle_multi!");returnNS_ERROR_FAILURE;}NS_ENSURE_SUCCESS(rv,rv);NS_IF_ADDREF(*aResult=wv.get());returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetFillStyle(nsIVariant*aValue){if(!aValue)returnNS_ERROR_FAILURE;nsStringstr;nsresultrv;PRUint16vtype;rv=aValue->GetDataType(&vtype);NS_ENSURE_SUCCESS(rv,rv);if(vtype==nsIDataType::VTYPE_INTERFACE||vtype==nsIDataType::VTYPE_INTERFACE_IS){nsIID*iid;nsCOMPtr<nsISupports>sup;rv=aValue->GetAsInterface(&iid,getter_AddRefs(sup));NS_ENSURE_SUCCESS(rv,rv);str.SetIsVoid(true);returnSetFillStyle_multi(str,sup);}rv=aValue->GetAsAString(str);NS_ENSURE_SUCCESS(rv,rv);returnSetFillStyle_multi(str,nsnull);}NS_IMETHODIMPnsCanvasRenderingContext2D::GetFillStyle(nsIVariant**aResult){nsCOMPtr<nsIWritableVariant>wv=do_CreateInstance(NS_VARIANT_CONTRACTID);nsCOMPtr<nsISupports>sup;nsStringstr;PRInt32t;nsresultrv=GetFillStyle_multi(str,getter_AddRefs(sup),&t);NS_ENSURE_SUCCESS(rv,rv);if(t==CMG_STYLE_STRING){rv=wv->SetAsAString(str);}elseif(t==CMG_STYLE_PATTERN){rv=wv->SetAsInterface(NS_GET_IID(nsIDOMCanvasPattern),sup);}elseif(t==CMG_STYLE_GRADIENT){rv=wv->SetAsInterface(NS_GET_IID(nsIDOMCanvasGradient),sup);}else{NS_ERROR("Unknown type from GetStroke/FillStyle_multi!");returnNS_ERROR_FAILURE;}NS_ENSURE_SUCCESS(rv,rv);NS_IF_ADDREF(*aResult=wv.get());returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetStrokeStyle_multi(constnsAString&aStr,nsISupports*aInterface){returnSetStyleFromStringOrInterface(aStr,aInterface,STYLE_STROKE);}NS_IMETHODIMPnsCanvasRenderingContext2D::GetStrokeStyle_multi(nsAString&aStr,nsISupports**aInterface,PRInt32*aType){returnGetStyleAsStringOrInterface(aStr,aInterface,aType,STYLE_STROKE);}NS_IMETHODIMPnsCanvasRenderingContext2D::SetFillStyle_multi(constnsAString&aStr,nsISupports*aInterface){returnSetStyleFromStringOrInterface(aStr,aInterface,STYLE_FILL);}NS_IMETHODIMPnsCanvasRenderingContext2D::GetFillStyle_multi(nsAString&aStr,nsISupports**aInterface,PRInt32*aType){returnGetStyleAsStringOrInterface(aStr,aInterface,aType,STYLE_FILL);}NS_IMETHODIMPnsCanvasRenderingContext2D::SetMozFillRule(constnsAString&aString){gfxContext::FillRulerule;if(!EnsureSurface())returnNS_ERROR_FAILURE;if(aString.EqualsLiteral("evenodd"))rule=gfxContext::FILL_RULE_EVEN_ODD;elseif(aString.EqualsLiteral("nonzero"))rule=gfxContext::FILL_RULE_WINDING;else// XXX ERRMSG we need to report an error to developers here! (bug 329026)returnNS_OK;mThebes->SetFillRule(rule);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetMozFillRule(nsAString&aString){if(!EnsureSurface())returnNS_ERROR_FAILURE;switch(mThebes->CurrentFillRule()){casegfxContext::FILL_RULE_WINDING:aString.AssignLiteral("nonzero");break;casegfxContext::FILL_RULE_EVEN_ODD:aString.AssignLiteral("evenodd");break;}returnNS_OK;}//// gradients and patterns//NS_IMETHODIMPnsCanvasRenderingContext2D::CreateLinearGradient(floatx0,floaty0,floatx1,floaty1,nsIDOMCanvasGradient**_retval){if(!FloatValidate(x0,y0,x1,y1))returnNS_ERROR_DOM_NOT_SUPPORTED_ERR;nsRefPtr<gfxPattern>gradpat=newgfxPattern(x0,y0,x1,y1);if(!gradpat)returnNS_ERROR_OUT_OF_MEMORY;nsRefPtr<nsIDOMCanvasGradient>grad=newnsCanvasGradient(gradpat);if(!grad)returnNS_ERROR_OUT_OF_MEMORY;*_retval=grad.forget().get();returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::CreateRadialGradient(floatx0,floaty0,floatr0,floatx1,floaty1,floatr1,nsIDOMCanvasGradient**_retval){if(!FloatValidate(x0,y0,r0,x1,y1,r1))returnNS_ERROR_DOM_NOT_SUPPORTED_ERR;if(r0<0.0||r1<0.0)returnNS_ERROR_DOM_INDEX_SIZE_ERR;nsRefPtr<gfxPattern>gradpat=newgfxPattern(x0,y0,r0,x1,y1,r1);if(!gradpat)returnNS_ERROR_OUT_OF_MEMORY;nsRefPtr<nsIDOMCanvasGradient>grad=newnsCanvasGradient(gradpat);if(!grad)returnNS_ERROR_OUT_OF_MEMORY;*_retval=grad.forget().get();returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::CreatePattern(nsIDOMHTMLElement*image,constnsAString&repeat,nsIDOMCanvasPattern**_retval){nsCOMPtr<nsIContent>content=do_QueryInterface(image);if(!content){returnNS_ERROR_DOM_TYPE_MISMATCH_ERR;}gfxPattern::GraphicsExtendextend;if(repeat.IsEmpty()||repeat.EqualsLiteral("repeat")){extend=gfxPattern::EXTEND_REPEAT;}elseif(repeat.EqualsLiteral("repeat-x")){// XXextend=gfxPattern::EXTEND_REPEAT;}elseif(repeat.EqualsLiteral("repeat-y")){// XXextend=gfxPattern::EXTEND_REPEAT;}elseif(repeat.EqualsLiteral("no-repeat")){extend=gfxPattern::EXTEND_NONE;}else{// XXX ERRMSG we need to report an error to developers here! (bug 329026)returnNS_ERROR_DOM_SYNTAX_ERR;}nsHTMLCanvasElement*canvas=nsHTMLCanvasElement::FromContent(content);if(canvas){nsIntSizesize=canvas->GetSize();if(size.width==0||size.height==0){returnNS_ERROR_DOM_INVALID_STATE_ERR;}}// The canvas spec says that createPattern should use the first frame// of animated imagesnsLayoutUtils::SurfaceFromElementResultres=nsLayoutUtils::SurfaceFromElement(content->AsElement(),nsLayoutUtils::SFE_WANT_FIRST_FRAME|nsLayoutUtils::SFE_WANT_NEW_SURFACE);if(!res.mSurface)returnNS_ERROR_NOT_AVAILABLE;nsRefPtr<gfxPattern>thebespat=newgfxPattern(res.mSurface);thebespat->SetExtend(extend);nsRefPtr<nsCanvasPattern>pat=newnsCanvasPattern(thebespat,res.mPrincipal,res.mIsWriteOnly,res.mCORSUsed);*_retval=pat.forget().get();returnNS_OK;}//// shadows//NS_IMETHODIMPnsCanvasRenderingContext2D::SetShadowOffsetX(floatx){if(!FloatValidate(x))returnNS_OK;CurrentState().shadowOffset.x=x;returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetShadowOffsetX(float*x){*x=static_cast<float>(CurrentState().shadowOffset.x);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetShadowOffsetY(floaty){if(!FloatValidate(y))returnNS_OK;CurrentState().shadowOffset.y=y;returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetShadowOffsetY(float*y){*y=static_cast<float>(CurrentState().shadowOffset.y);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetShadowBlur(floatblur){if(!FloatValidate(blur)||blur<0.0)returnNS_OK;CurrentState().shadowBlur=blur;returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetShadowBlur(float*blur){*blur=CurrentState().shadowBlur;returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetShadowColor(constnsAString&colorstr){nsIDocument*document=mCanvasElement?HTMLCanvasElement()->OwnerDoc():nsnull;// Pass the CSS Loader object to the parser, to allow parser error reports// to include the outer window ID.nsCSSParserparser(document?document->CSSLoader():nsnull);nscolorcolor;nsresultrv=parser.ParseColorString(colorstr,nsnull,0,&color);if(NS_FAILED(rv)){// Error reporting happens inside the CSS parserreturnNS_OK;}CurrentState().SetColorStyle(STYLE_SHADOW,color);mDirtyStyle[STYLE_SHADOW]=true;returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetShadowColor(nsAString&color){StyleColorToString(CurrentState().colorStyles[STYLE_SHADOW],color);returnNS_OK;}staticconstgfxFloatSIGMA_MAX=100;gfxContext*nsCanvasRenderingContext2D::ShadowInitialize(constgfxRect&extents,gfxAlphaBoxBlur&blur){gfxIntSizeblurRadius;floatshadowBlur=CurrentState().shadowBlur;gfxFloatsigma=shadowBlur/2;// limit to avoid overly huge temp imagesif(sigma>SIGMA_MAX)sigma=SIGMA_MAX;blurRadius=gfxAlphaBoxBlur::CalculateBlurRadius(gfxPoint(sigma,sigma));// calculate extentsgfxRectdrawExtents=extents;// intersect with clip to avoid making overly huge temp imagesgfxMatrixmatrix=mThebes->CurrentMatrix();mThebes->IdentityMatrix();gfxRectclipExtents=mThebes->GetClipExtents();mThebes->SetMatrix(matrix);// outset by the blur radius so that blurs can leak onto the canvas even// when the shape is outside the clipping areaclipExtents.Inflate(blurRadius.width,blurRadius.height);drawExtents=drawExtents.Intersect(clipExtents-CurrentState().shadowOffset);gfxContext*ctx=blur.Init(drawExtents,gfxIntSize(0,0),blurRadius,nsnull,nsnull);if(!ctx)returnnsnull;returnctx;}voidnsCanvasRenderingContext2D::ShadowFinalize(gfxAlphaBoxBlur&blur){if(!EnsureSurface())return;ApplyStyle(STYLE_SHADOW);// canvas matrix was already applied, don't apply it twice, but do// apply the shadow offsetgfxMatrixmatrix=mThebes->CurrentMatrix();mThebes->IdentityMatrix();mThebes->Translate(CurrentState().shadowOffset);blur.Paint(mThebes);mThebes->SetMatrix(matrix);}nsresultnsCanvasRenderingContext2D::DrawPath(Stylestyle,gfxRect*dirtyRect){if(!EnsureSurface())returnNS_ERROR_FAILURE;booldoUseIntermediateSurface=false;if(mSurface->GetType()==gfxASurface::SurfaceTypeD2D){if(style!=STYLE_FILL){// D2D does all operators correctly even if transparent areas of SOURCE// affect dest. We need to use an intermediate surface for STROKE because// we can't clip to the actual stroke shape easily, but prefer a geometric// clip over an intermediate surface for a FILL.doUseIntermediateSurface=NeedIntermediateSurfaceToHandleGlobalAlpha(style);}}else{/* * Need an intermediate surface when: * - globalAlpha != 1 and gradients/patterns are used (need to paint_with_alpha) * - certain operators are used */doUseIntermediateSurface=NeedToUseIntermediateSurface()||NeedIntermediateSurfaceToHandleGlobalAlpha(style);}booldoDrawShadow=NeedToDrawShadow();// Clear the surface if we need to simulate unbounded SOURCE operatorClearSurfaceForUnboundedSource();if(doDrawShadow){gfxMatrixmatrix=mThebes->CurrentMatrix();mThebes->IdentityMatrix();// calculate extents of pathgfxRectdrawExtents;if(style==STYLE_FILL)drawExtents=mThebes->GetUserFillExtent();else// STYLE_STROKEdrawExtents=mThebes->GetUserStrokeExtent();mThebes->SetMatrix(matrix);gfxAlphaBoxBlurblur;// no need for a ref here, the blur owns the contextgfxContext*ctx=ShadowInitialize(drawExtents,blur);if(ctx){ApplyStyle(style,false);CopyContext(ctx,mThebes);ctx->SetOperator(gfxContext::OPERATOR_SOURCE);if(style==STYLE_FILL)ctx->Fill();elsectx->Stroke();ShadowFinalize(blur);}}if(doUseIntermediateSurface){nsRefPtr<gfxPath>path=mThebes->CopyPath();// if the path didn't copy correctly then we can't restore it, so bailif(!path)returnNS_ERROR_FAILURE;// draw onto a pushed groupmThebes->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);// XXX for some reason clipping messes up the path when push/popping// copying the path seems to fix it, for unknown reasonsmThebes->NewPath();mThebes->AppendPath(path);// don't want operators to be applied twice,if(mSurface->GetType()!=gfxASurface::SurfaceTypeD2D){mThebes->SetOperator(gfxContext::OPERATOR_SOURCE);}else{// In the case of D2D OPERATOR_OVER is much faster. So we can just// use that since it's the same as SOURCE for a transparent// destinations. It would be nice if cairo backends could make this// optimization internally but I see no very good way of doing this.mThebes->SetOperator(gfxContext::OPERATOR_OVER);}}ApplyStyle(style);if(style==STYLE_FILL){if(!doUseIntermediateSurface&&CurrentState().globalAlpha!=1.0&&!CurrentState().StyleIsColor(style)){mThebes->Clip();mThebes->Paint(CurrentState().globalAlpha);}else{mThebes->Fill();}}elsemThebes->Stroke();// XXX do some more work to calculate the extents of shadows// XXX handle stroke extentsif(dirtyRect&&style==STYLE_FILL&&!doDrawShadow){*dirtyRect=mThebes->GetUserPathExtent();}if(doUseIntermediateSurface){mThebes->PopGroupToSource();DirtyAllStyles();mThebes->Paint(CurrentState().StyleIsColor(style)?1.0:CurrentState().globalAlpha);}if(dirtyRect){if(style!=STYLE_FILL||doDrawShadow){// just use the clip extents*dirtyRect=mThebes->GetClipExtents();}}returnNS_OK;}//// rects//NS_IMETHODIMPnsCanvasRenderingContext2D::ClearRect(floatx,floaty,floatw,floath){if(!mSurfaceCreated)returnNS_OK;if(!FloatValidate(x,y,w,h))returnNS_OK;PathAutoSaveRestorepathSR(this);gfxContextAutoSaveRestoreautoSR(mThebes);mThebes->SetOperator(gfxContext::OPERATOR_CLEAR);mThebes->NewPath();mThebes->Rectangle(gfxRect(x,y,w,h));mThebes->Fill();returnRedrawUser(mThebes->GetUserPathExtent());}nsresultnsCanvasRenderingContext2D::DrawRect(constgfxRect&rect,Stylestyle){if(!EnsureSurface())returnNS_ERROR_FAILURE;if(!FloatValidate(rect.X(),rect.Y(),rect.Width(),rect.Height()))returnNS_OK;PathAutoSaveRestorepathSR(this);mThebes->NewPath();mThebes->Rectangle(rect);gfxRectdirty;nsresultrv=DrawPath(style,&dirty);if(NS_FAILED(rv))returnrv;returnRedrawUser(dirty);}NS_IMETHODIMPnsCanvasRenderingContext2D::FillRect(floatx,floaty,floatw,floath){returnDrawRect(gfxRect(x,y,w,h),STYLE_FILL);}NS_IMETHODIMPnsCanvasRenderingContext2D::StrokeRect(floatx,floaty,floatw,floath){if(w==0.f&&h==0.f){returnNS_OK;}returnDrawRect(gfxRect(x,y,w,h),STYLE_STROKE);}//// path bits//NS_IMETHODIMPnsCanvasRenderingContext2D::BeginPath(){if(!EnsureSurface())returnNS_ERROR_FAILURE;mHasPath=false;mThebes->NewPath();returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::ClosePath(){if(!EnsureSurface())returnNS_ERROR_FAILURE;mThebes->ClosePath();returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::Fill(){gfxRectdirty;nsresultrv=DrawPath(STYLE_FILL,&dirty);if(NS_FAILED(rv))returnrv;returnRedrawUser(dirty);}NS_IMETHODIMPnsCanvasRenderingContext2D::Stroke(){gfxRectdirty;nsresultrv=DrawPath(STYLE_STROKE,&dirty);if(NS_FAILED(rv))returnrv;returnRedrawUser(dirty);}NS_IMETHODIMPnsCanvasRenderingContext2D::Clip(){if(!EnsureSurface())returnNS_ERROR_FAILURE;mThebes->Clip();returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::MoveTo(floatx,floaty){if(!EnsureSurface())returnNS_ERROR_FAILURE;if(!FloatValidate(x,y))returnNS_OK;mHasPath=true;mThebes->MoveTo(gfxPoint(x,y));returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::LineTo(floatx,floaty){if(!EnsureSurface())returnNS_ERROR_FAILURE;if(!FloatValidate(x,y))returnNS_OK;mHasPath=true;mThebes->LineTo(gfxPoint(x,y));returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::QuadraticCurveTo(floatcpx,floatcpy,floatx,floaty){if(!EnsureSurface())returnNS_ERROR_FAILURE;if(!FloatValidate(cpx,cpy,x,y))returnNS_OK;// we will always have a current point, since beginPath forces// a moveto(0,0)gfxPointc=mThebes->CurrentPoint();gfxPointp(x,y);gfxPointcp(cpx,cpy);mHasPath=true;mThebes->CurveTo((c+cp*2)/3.0,(p+cp*2)/3.0,p);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::BezierCurveTo(floatcp1x,floatcp1y,floatcp2x,floatcp2y,floatx,floaty){if(!EnsureSurface())returnNS_ERROR_FAILURE;if(!FloatValidate(cp1x,cp1y,cp2x,cp2y,x,y))returnNS_OK;mHasPath=true;mThebes->CurveTo(gfxPoint(cp1x,cp1y),gfxPoint(cp2x,cp2y),gfxPoint(x,y));returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::ArcTo(floatx1,floaty1,floatx2,floaty2,floatradius){if(!EnsureSurface())returnNS_ERROR_FAILURE;if(!FloatValidate(x1,y1,x2,y2,radius))returnNS_OK;if(radius<0)returnNS_ERROR_DOM_INDEX_SIZE_ERR;mHasPath=true;gfxPointp0=mThebes->CurrentPoint();doubledir,a2,b2,c2,cosx,sinx,d,anx,any,bnx,bny,x3,y3,x4,y4,cx,cy,angle0,angle1;boolanticlockwise;if((x1==p0.x&&y1==p0.y)||(x1==x2&&y1==y2)||radius==0){mThebes->LineTo(gfxPoint(x1,y1));returnNS_OK;}dir=(x2-x1)*(p0.y-y1)+(y2-y1)*(x1-p0.x);if(dir==0){mThebes->LineTo(gfxPoint(x1,y1));returnNS_OK;}a2=(p0.x-x1)*(p0.x-x1)+(p0.y-y1)*(p0.y-y1);b2=(x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);c2=(p0.x-x2)*(p0.x-x2)+(p0.y-y2)*(p0.y-y2);cosx=(a2+b2-c2)/(2*sqrt(a2*b2));sinx=sqrt(1-cosx*cosx);d=radius/((1-cosx)/sinx);anx=(x1-p0.x)/sqrt(a2);any=(y1-p0.y)/sqrt(a2);bnx=(x1-x2)/sqrt(b2);bny=(y1-y2)/sqrt(b2);x3=x1-anx*d;y3=y1-any*d;x4=x1-bnx*d;y4=y1-bny*d;anticlockwise=(dir<0);cx=x3+any*radius*(anticlockwise?1:-1);cy=y3-anx*radius*(anticlockwise?1:-1);angle0=atan2((y3-cy),(x3-cx));angle1=atan2((y4-cy),(x4-cx));mThebes->LineTo(gfxPoint(x3,y3));if(anticlockwise)mThebes->NegativeArc(gfxPoint(cx,cy),radius,angle0,angle1);elsemThebes->Arc(gfxPoint(cx,cy),radius,angle0,angle1);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::Arc(floatx,floaty,floatr,floatstartAngle,floatendAngle,boolccw){if(!EnsureSurface())returnNS_ERROR_FAILURE;if(!FloatValidate(x,y,r,startAngle,endAngle))returnNS_OK;if(r<0.0)returnNS_ERROR_DOM_INDEX_SIZE_ERR;gfxPointp(x,y);mHasPath=true;if(ccw)mThebes->NegativeArc(p,r,startAngle,endAngle);elsemThebes->Arc(p,r,startAngle,endAngle);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::Rect(floatx,floaty,floatw,floath){if(!EnsureSurface())returnNS_ERROR_FAILURE;if(!FloatValidate(x,y,w,h))returnNS_OK;mHasPath=true;mThebes->Rectangle(gfxRect(x,y,w,h));returnNS_OK;}//// text///** * Helper function for SetFont that creates a style rule for the given font. * @param aFont The CSS font string * @param aNode The canvas element * @param aResult Pointer in which to place the new style rule. * @remark Assumes all pointer arguments are non-null. */staticnsresultCreateFontStyleRule(constnsAString&aFont,nsINode*aNode,css::StyleRule**aResult){nsRefPtr<css::StyleRule>rule;boolchanged;nsIPrincipal*principal=aNode->NodePrincipal();nsIDocument*document=aNode->OwnerDoc();nsIURI*docURL=document->GetDocumentURI();nsIURI*baseURL=document->GetDocBaseURI();// Pass the CSS Loader object to the parser, to allow parser error reports// to include the outer window ID.nsCSSParserparser(document->CSSLoader());nsresultrv=parser.ParseStyleAttribute(EmptyString(),docURL,baseURL,principal,getter_AddRefs(rule));if(NS_FAILED(rv))returnrv;rv=parser.ParseProperty(eCSSProperty_font,aFont,docURL,baseURL,principal,rule->GetDeclaration(),&changed,false);if(NS_FAILED(rv))returnrv;rv=parser.ParseProperty(eCSSProperty_line_height,NS_LITERAL_STRING("normal"),docURL,baseURL,principal,rule->GetDeclaration(),&changed,false);if(NS_FAILED(rv))returnrv;rule->RuleMatched();rule.forget(aResult);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetFont(constnsAString&font){nsresultrv;/* * If font is defined with relative units (e.g. ems) and the parent * style context changes in between calls, setting the font to the * same value as previous could result in a different computed value, * so we cannot have the optimization where we check if the new font * string is equal to the old one. */nsCOMPtr<nsIContent>content=do_QueryInterface(mCanvasElement);if(!content&&!mDocShell){NS_WARNING("Canvas element must be an nsIContent and non-null or a docshell must be provided");returnNS_ERROR_FAILURE;}nsIPresShell*presShell=GetPresShell();if(!presShell)returnNS_ERROR_FAILURE;nsIDocument*document=presShell->GetDocument();nsCOMArray<nsIStyleRule>rules;nsRefPtr<css::StyleRule>rule;rv=CreateFontStyleRule(font,document,getter_AddRefs(rule));if(NS_FAILED(rv))returnrv;css::Declaration*declaration=rule->GetDeclaration();// The easiest way to see whether we got a syntax error or whether// we got 'inherit' or 'initial' is to look at font-size-adjust,// which the shorthand resets to either 'none' or// '-moz-system-font'.// We know the declaration is not !important, so we can use// GetNormalBlock().constnsCSSValue*fsaVal=declaration->GetNormalBlock()->ValueFor(eCSSProperty_font_size_adjust);if(!fsaVal||(fsaVal->GetUnit()!=eCSSUnit_None&&fsaVal->GetUnit()!=eCSSUnit_System_Font)){// We got an all-property value or a syntax error. The spec says// this value must be ignored.returnNS_OK;}rules.AppendObject(rule);nsStyleSet*styleSet=presShell->StyleSet();// have to get a parent style context for inherit-like relative// values (2em, bolder, etc.)nsRefPtr<nsStyleContext>parentContext;if(content&&content->IsInDoc()){// inherit from the canvas elementparentContext=nsComputedDOMStyle::GetStyleContextForElement(content->AsElement(),nsnull,presShell);}else{// otherwise inherit from defaultnsRefPtr<css::StyleRule>parentRule;rv=CreateFontStyleRule(kDefaultFontStyle,document,getter_AddRefs(parentRule));if(NS_FAILED(rv))returnrv;nsCOMArray<nsIStyleRule>parentRules;parentRules.AppendObject(parentRule);parentContext=styleSet->ResolveStyleForRules(nsnull,parentRules);}if(!parentContext)returnNS_ERROR_FAILURE;nsRefPtr<nsStyleContext>sc=styleSet->ResolveStyleForRules(parentContext,rules);if(!sc)returnNS_ERROR_FAILURE;constnsStyleFont*fontStyle=sc->GetStyleFont();NS_ASSERTION(fontStyle,"Could not obtain font style");nsIAtom*language=sc->GetStyleFont()->mLanguage;if(!language){language=presShell->GetPresContext()->GetLanguageFromCharset();}// use CSS pixels instead of dev pixels to avoid being affected by page zoomconstPRUint32aupcp=nsPresContext::AppUnitsPerCSSPixel();// un-zoom the font size to avoid being affected by text-only zoom//// Purposely ignore the font size that respects the user's minimum// font preference (fontStyle->mFont.size) in favor of the// computed size (fontStyle->mSize). See// https://bugzilla.mozilla.org/show_bug.cgi?id=698652.constnscoordfontSize=nsStyleFont::UnZoomText(parentContext->PresContext(),fontStyle->mSize);boolprinterFont=(presShell->GetPresContext()->Type()==nsPresContext::eContext_PrintPreview||presShell->GetPresContext()->Type()==nsPresContext::eContext_Print);gfxFontStylestyle(fontStyle->mFont.style,fontStyle->mFont.weight,fontStyle->mFont.stretch,NSAppUnitsToFloatPixels(fontSize,float(aupcp)),language,fontStyle->mFont.sizeAdjust,fontStyle->mFont.systemFont,printerFont,fontStyle->mFont.languageOverride);fontStyle->mFont.AddFontFeaturesToStyle(&style);CurrentState().fontGroup=gfxPlatform::GetPlatform()->CreateFontGroup(fontStyle->mFont.name,&style,presShell->GetPresContext()->GetUserFontSet());NS_ASSERTION(CurrentState().fontGroup,"Could not get font group");// The font getter is required to be reserialized based on what we// parsed (including having line-height removed). (Older drafts of// the spec required font sizes be converted to pixels, but that no// longer seems to be required.)declaration->GetValue(eCSSProperty_font,CurrentState().font);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetFont(nsAString&font){/* will initilize the value if not set, else does nothing */GetCurrentFontStyle();font=CurrentState().font;returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetTextAlign(constnsAString&ta){if(ta.EqualsLiteral("start"))CurrentState().textAlign=TEXT_ALIGN_START;elseif(ta.EqualsLiteral("end"))CurrentState().textAlign=TEXT_ALIGN_END;elseif(ta.EqualsLiteral("left"))CurrentState().textAlign=TEXT_ALIGN_LEFT;elseif(ta.EqualsLiteral("right"))CurrentState().textAlign=TEXT_ALIGN_RIGHT;elseif(ta.EqualsLiteral("center"))CurrentState().textAlign=TEXT_ALIGN_CENTER;returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetTextAlign(nsAString&ta){switch(CurrentState().textAlign){caseTEXT_ALIGN_START:ta.AssignLiteral("start");break;caseTEXT_ALIGN_END:ta.AssignLiteral("end");break;caseTEXT_ALIGN_LEFT:ta.AssignLiteral("left");break;caseTEXT_ALIGN_RIGHT:ta.AssignLiteral("right");break;caseTEXT_ALIGN_CENTER:ta.AssignLiteral("center");break;}returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetTextBaseline(constnsAString&tb){if(tb.EqualsLiteral("top"))CurrentState().textBaseline=TEXT_BASELINE_TOP;elseif(tb.EqualsLiteral("hanging"))CurrentState().textBaseline=TEXT_BASELINE_HANGING;elseif(tb.EqualsLiteral("middle"))CurrentState().textBaseline=TEXT_BASELINE_MIDDLE;elseif(tb.EqualsLiteral("alphabetic"))CurrentState().textBaseline=TEXT_BASELINE_ALPHABETIC;elseif(tb.EqualsLiteral("ideographic"))CurrentState().textBaseline=TEXT_BASELINE_IDEOGRAPHIC;elseif(tb.EqualsLiteral("bottom"))CurrentState().textBaseline=TEXT_BASELINE_BOTTOM;returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetTextBaseline(nsAString&tb){switch(CurrentState().textBaseline){caseTEXT_BASELINE_TOP:tb.AssignLiteral("top");break;caseTEXT_BASELINE_HANGING:tb.AssignLiteral("hanging");break;caseTEXT_BASELINE_MIDDLE:tb.AssignLiteral("middle");break;caseTEXT_BASELINE_ALPHABETIC:tb.AssignLiteral("alphabetic");break;caseTEXT_BASELINE_IDEOGRAPHIC:tb.AssignLiteral("ideographic");break;caseTEXT_BASELINE_BOTTOM:tb.AssignLiteral("bottom");break;}returnNS_OK;}/* * Helper function that replaces the whitespace characters in a string * with U+0020 SPACE. The whitespace characters are defined as U+0020 SPACE, * U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF), U+000B LINE * TABULATION, U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR). * @param str The string whose whitespace characters to replace. */staticinlinevoidTextReplaceWhitespaceCharacters(nsAutoString&str){str.ReplaceChar("\x09\x0A\x0B\x0C\x0D",PRUnichar(' '));}NS_IMETHODIMPnsCanvasRenderingContext2D::FillText(constnsAString&text,floatx,floaty,floatmaxWidth){returnDrawOrMeasureText(text,x,y,maxWidth,TEXT_DRAW_OPERATION_FILL,nsnull);}NS_IMETHODIMPnsCanvasRenderingContext2D::StrokeText(constnsAString&text,floatx,floaty,floatmaxWidth){returnDrawOrMeasureText(text,x,y,maxWidth,TEXT_DRAW_OPERATION_STROKE,nsnull);}NS_IMETHODIMPnsCanvasRenderingContext2D::MeasureText(constnsAString&rawText,nsIDOMTextMetrics**_retval){floatwidth;nsresultrv=DrawOrMeasureText(rawText,0,0,0,TEXT_DRAW_OPERATION_MEASURE,&width);if(NS_FAILED(rv))returnrv;nsRefPtr<nsIDOMTextMetrics>textMetrics=newnsTextMetrics(width);if(!textMetrics.get())returnNS_ERROR_OUT_OF_MEMORY;*_retval=textMetrics.forget().get();returnNS_OK;}/** * Used for nsBidiPresUtils::ProcessText */structNS_STACK_CLASSnsCanvasBidiProcessor:publicnsBidiPresUtils::BidiProcessor{virtualvoidSetText(constPRUnichar*text,PRInt32length,nsBidiDirectiondirection){mFontgrp->UpdateFontList();// ensure user font generation is currentmTextRun=mFontgrp->MakeTextRun(text,length,mThebes,mAppUnitsPerDevPixel,direction==NSBIDI_RTL?gfxTextRunFactory::TEXT_IS_RTL:0);}virtualnscoordGetWidth(){gfxTextRun::MetricstextRunMetrics=mTextRun->MeasureText(0,mTextRun->GetLength(),mDoMeasureBoundingBox?gfxFont::TIGHT_INK_EXTENTS:gfxFont::LOOSE_INK_EXTENTS,mThebes,nsnull);// this only measures the height; the total width is gotten from the// the return value of ProcessText.if(mDoMeasureBoundingBox){textRunMetrics.mBoundingBox.Scale(1.0/mAppUnitsPerDevPixel);mBoundingBox=mBoundingBox.Union(textRunMetrics.mBoundingBox);}returnNSToCoordRound(textRunMetrics.mAdvanceWidth);}virtualvoidDrawText(nscoordxOffset,nscoordwidth){gfxPointpoint=mPt;point.x+=xOffset;// offset is given in terms of left side of stringif(mTextRun->IsRightToLeft()){// Bug 581092 - don't use rounded pixel width to advance to// right-hand end of run, because this will cause different// glyph positioning for LTR vs RTL drawing of the same// glyph string on OS X and DWrite where textrun widths may// involve fractional pixels.gfxTextRun::MetricstextRunMetrics=mTextRun->MeasureText(0,mTextRun->GetLength(),mDoMeasureBoundingBox?gfxFont::TIGHT_INK_EXTENTS:gfxFont::LOOSE_INK_EXTENTS,mThebes,nsnull);point.x+=textRunMetrics.mAdvanceWidth;// old code was:// point.x += width * mAppUnitsPerDevPixel;// TODO: restore this if/when we move to fractional coords// throughout the text layout process}mTextRun->Draw(mThebes,point,mOp==nsCanvasRenderingContext2D::TEXT_DRAW_OPERATION_STROKE?gfxFont::GLYPH_STROKE:gfxFont::GLYPH_FILL,0,mTextRun->GetLength(),nsnull,nsnull,nsnull);}// current text runnsAutoPtr<gfxTextRun>mTextRun;// pointer to the context, may not be the canvas's context// if an intermediate surface is being usedgfxContext*mThebes;// position of the left side of the string, alphabetic baselinegfxPointmPt;// current fontgfxFontGroup*mFontgrp;// dev pixel conversion factorPRUint32mAppUnitsPerDevPixel;// operation (fill or stroke)nsCanvasRenderingContext2D::TextDrawOperationmOp;// union of bounding boxes of all runs, needed for shadowsgfxRectmBoundingBox;// true iff the bounding box should be measuredboolmDoMeasureBoundingBox;};nsresultnsCanvasRenderingContext2D::DrawOrMeasureText(constnsAString&aRawText,floataX,floataY,floataMaxWidth,TextDrawOperationaOp,float*aWidth){nsresultrv;if(!FloatValidate(aX,aY,aMaxWidth))returnNS_ERROR_DOM_SYNTAX_ERR;// spec isn't clear on what should happen if aMaxWidth <= 0, so// treat it as an invalid argument// technically, 0 should be an invalid value as well, but 0 is the default// arg, and there is no way to tell if the default was usedif(aMaxWidth<0)returnNS_ERROR_INVALID_ARG;nsCOMPtr<nsIContent>content=do_QueryInterface(mCanvasElement);if(!content&&!mDocShell){NS_WARNING("Canvas element must be an nsIContent and non-null or a docshell must be provided");returnNS_ERROR_FAILURE;}nsCOMPtr<nsIPresShell>presShell=GetPresShell();if(!presShell)returnNS_ERROR_FAILURE;nsIDocument*document=presShell->GetDocument();// replace all the whitespace characters with U+0020 SPACEnsAutoStringtextToDraw(aRawText);TextReplaceWhitespaceCharacters(textToDraw);// for now, default to ltr if not in docboolisRTL=false;if(content&&content->IsInDoc()){// try to find the closest contextnsRefPtr<nsStyleContext>canvasStyle=nsComputedDOMStyle::GetStyleContextForElement(content->AsElement(),nsnull,presShell);if(!canvasStyle)returnNS_ERROR_FAILURE;isRTL=canvasStyle->GetStyleVisibility()->mDirection==NS_STYLE_DIRECTION_RTL;}else{isRTL=GET_BIDI_OPTION_DIRECTION(document->GetBidiOptions())==IBMBIDI_TEXTDIRECTION_RTL;}Stylestyle=aOp==TEXT_DRAW_OPERATION_FILL?STYLE_FILL:STYLE_STROKE;booldoDrawShadow=NeedToDrawShadow();booldoUseIntermediateSurface=NeedToUseIntermediateSurface()||NeedIntermediateSurfaceToHandleGlobalAlpha(style);// Clear the surface if we need to simulate unbounded SOURCE operatorClearSurfaceForUnboundedSource();nsCanvasBidiProcessorprocessor;GetAppUnitsValues(&processor.mAppUnitsPerDevPixel,NULL);processor.mPt=gfxPoint(aX,aY);nsRefPtr<nsRenderingContext>ctx;if(mThebes){processor.mThebes=mThebes;}else{ctx=presShell->GetReferenceRenderingContext();processor.mThebes=ctx->ThebesContext();}processor.mOp=aOp;processor.mBoundingBox=gfxRect(0,0,0,0);processor.mDoMeasureBoundingBox=doDrawShadow||!mIsEntireFrameInvalid;processor.mFontgrp=GetCurrentFontStyle();NS_ASSERTION(processor.mFontgrp,"font group is null");nscoordtotalWidthCoord;// calls bidi algo twice since it needs the full text width and the// bounding boxes before rendering anythingnsBidibidiEngine;rv=nsBidiPresUtils::ProcessText(textToDraw.get(),textToDraw.Length(),isRTL?NSBIDI_RTL:NSBIDI_LTR,presShell->GetPresContext(),processor,nsBidiPresUtils::MODE_MEASURE,nsnull,0,&totalWidthCoord,&bidiEngine);if(NS_FAILED(rv))returnrv;floattotalWidth=float(totalWidthCoord)/processor.mAppUnitsPerDevPixel;if(aWidth)*aWidth=totalWidth;// if only measuring, don't need to do any more workif(aOp==TEXT_DRAW_OPERATION_MEASURE)returnNS_OK;if(!EnsureSurface())returnNS_ERROR_FAILURE;processor.mThebes=mThebes;// offset pt.x based on text aligngfxFloatanchorX;if(CurrentState().textAlign==TEXT_ALIGN_CENTER)anchorX=.5;elseif(CurrentState().textAlign==TEXT_ALIGN_LEFT||(!isRTL&&CurrentState().textAlign==TEXT_ALIGN_START)||(isRTL&&CurrentState().textAlign==TEXT_ALIGN_END))anchorX=0;elseanchorX=1;processor.mPt.x-=anchorX*totalWidth;// offset pt.y based on text baselineprocessor.mFontgrp->UpdateFontList();// ensure user font generation is currentNS_ASSERTION(processor.mFontgrp->FontListLength()>0,"font group contains no fonts");constgfxFont::Metrics&fontMetrics=processor.mFontgrp->GetFontAt(0)->GetMetrics();gfxFloatanchorY;switch(CurrentState().textBaseline){caseTEXT_BASELINE_HANGING:// fall through; best we can do with the information availablecaseTEXT_BASELINE_TOP:anchorY=fontMetrics.emAscent;break;break;caseTEXT_BASELINE_MIDDLE:anchorY=(fontMetrics.emAscent-fontMetrics.emDescent)*.5f;break;caseTEXT_BASELINE_IDEOGRAPHIC:// fall through; best we can do with the information availablecaseTEXT_BASELINE_ALPHABETIC:anchorY=0;break;caseTEXT_BASELINE_BOTTOM:anchorY=-fontMetrics.emDescent;break;}processor.mPt.y+=anchorY;// correct bounding box to get it to be the correct size/positionprocessor.mBoundingBox.width=totalWidth;processor.mBoundingBox.MoveBy(processor.mPt);processor.mPt.x*=processor.mAppUnitsPerDevPixel;processor.mPt.y*=processor.mAppUnitsPerDevPixel;// if text is over aMaxWidth, then scale the text horizontally such that its// width is precisely aMaxWidthgfxContextAutoSaveRestoreautoSR;if(aMaxWidth>0&&totalWidth>aMaxWidth){autoSR.SetContext(mThebes);// translate the anchor point to 0, then scale and translate backgfxPointtrans(aX,0);mThebes->Translate(trans);mThebes->Scale(aMaxWidth/totalWidth,1);mThebes->Translate(-trans);}// save the previous bounding boxgfxRectboundingBox=processor.mBoundingBox;// don't ever need to measure the bounding box twiceprocessor.mDoMeasureBoundingBox=false;if(doDrawShadow){// for some reason the box is too tight, probably rounding errorprocessor.mBoundingBox.Inflate(2.0);// this is unnecessarily big is max-width scaling is involved, but it// will still produce correct outputgfxRectdrawExtents=mThebes->UserToDevice(processor.mBoundingBox);gfxAlphaBoxBlurblur;gfxContext*ctx=ShadowInitialize(drawExtents,blur);if(ctx){ApplyStyle(style,false);CopyContext(ctx,mThebes);ctx->SetOperator(gfxContext::OPERATOR_SOURCE);processor.mThebes=ctx;rv=nsBidiPresUtils::ProcessText(textToDraw.get(),textToDraw.Length(),isRTL?NSBIDI_RTL:NSBIDI_LTR,presShell->GetPresContext(),processor,nsBidiPresUtils::MODE_DRAW,nsnull,0,nsnull,&bidiEngine);if(NS_FAILED(rv))returnrv;ShadowFinalize(blur);}processor.mThebes=mThebes;}gfxContextPathAutoSaveRestorepathSR(mThebes,false);if(doUseIntermediateSurface){mThebes->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);// don't want operators to be applied twicemThebes->SetOperator(gfxContext::OPERATOR_SOURCE);}ApplyStyle(style);rv=nsBidiPresUtils::ProcessText(textToDraw.get(),textToDraw.Length(),isRTL?NSBIDI_RTL:NSBIDI_LTR,presShell->GetPresContext(),processor,nsBidiPresUtils::MODE_DRAW,nsnull,0,nsnull,&bidiEngine);// this needs to be restored before function can returnif(doUseIntermediateSurface){mThebes->PopGroupToSource();DirtyAllStyles();}if(NS_FAILED(rv))returnrv;if(doUseIntermediateSurface)mThebes->Paint(CurrentState().StyleIsColor(style)?1.0:CurrentState().globalAlpha);if(aOp==nsCanvasRenderingContext2D::TEXT_DRAW_OPERATION_FILL&&!doDrawShadow)returnRedrawUser(boundingBox);returnRedraw();}NS_IMETHODIMPnsCanvasRenderingContext2D::SetMozTextStyle(constnsAString&textStyle){// font and mozTextStyle are the same valuereturnSetFont(textStyle);}NS_IMETHODIMPnsCanvasRenderingContext2D::GetMozTextStyle(nsAString&textStyle){// font and mozTextStyle are the same valuereturnGetFont(textStyle);}gfxFontGroup*nsCanvasRenderingContext2D::GetCurrentFontStyle(){// use lazy initilization for the font group since it's rather expensiveif(!CurrentState().fontGroup){nsresultrv=SetMozTextStyle(kDefaultFontStyle);if(NS_FAILED(rv)){gfxFontStylestyle;style.size=kDefaultFontSize;CurrentState().fontGroup=gfxPlatform::GetPlatform()->CreateFontGroup(kDefaultFontName,&style,nsnull);if(CurrentState().fontGroup){CurrentState().font=kDefaultFontStyle;rv=NS_OK;}else{rv=NS_ERROR_OUT_OF_MEMORY;}}NS_ASSERTION(NS_SUCCEEDED(rv),"Default canvas font is invalid");}returnCurrentState().fontGroup;}gfxTextRun*nsCanvasRenderingContext2D::MakeTextRun(constPRUnichar*aText,PRUint32aLength,PRUint32aAppUnitsPerDevUnit,PRUint32aFlags){gfxFontGroup*currentFontStyle=GetCurrentFontStyle();if(!currentFontStyle)returnnsnull;currentFontStyle->UpdateFontList();// ensure user font generation is currentreturncurrentFontStyle->MakeTextRun(aText,aLength,mThebes,aAppUnitsPerDevUnit,aFlags);}//// line caps/joins//NS_IMETHODIMPnsCanvasRenderingContext2D::SetLineWidth(floatwidth){if(!EnsureSurface())returnNS_ERROR_FAILURE;if(!FloatValidate(width)||width<=0.0)returnNS_OK;mThebes->SetLineWidth(width);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetLineWidth(float*width){if(!EnsureSurface())returnNS_ERROR_FAILURE;gfxFloatd=mThebes->CurrentLineWidth();*width=static_cast<float>(d);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetLineCap(constnsAString&capstyle){if(!EnsureSurface())returnNS_ERROR_FAILURE;gfxContext::GraphicsLineCapcap;if(capstyle.EqualsLiteral("butt"))cap=gfxContext::LINE_CAP_BUTT;elseif(capstyle.EqualsLiteral("round"))cap=gfxContext::LINE_CAP_ROUND;elseif(capstyle.EqualsLiteral("square"))cap=gfxContext::LINE_CAP_SQUARE;else// XXX ERRMSG we need to report an error to developers here! (bug 329026)returnNS_OK;mThebes->SetLineCap(cap);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetLineCap(nsAString&capstyle){if(!EnsureSurface())returnNS_ERROR_FAILURE;gfxContext::GraphicsLineCapcap=mThebes->CurrentLineCap();if(cap==gfxContext::LINE_CAP_BUTT)capstyle.AssignLiteral("butt");elseif(cap==gfxContext::LINE_CAP_ROUND)capstyle.AssignLiteral("round");elseif(cap==gfxContext::LINE_CAP_SQUARE)capstyle.AssignLiteral("square");elsereturnNS_ERROR_FAILURE;returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetLineJoin(constnsAString&joinstyle){if(!EnsureSurface())returnNS_ERROR_FAILURE;gfxContext::GraphicsLineJoinj;if(joinstyle.EqualsLiteral("round"))j=gfxContext::LINE_JOIN_ROUND;elseif(joinstyle.EqualsLiteral("bevel"))j=gfxContext::LINE_JOIN_BEVEL;elseif(joinstyle.EqualsLiteral("miter"))j=gfxContext::LINE_JOIN_MITER;else// XXX ERRMSG we need to report an error to developers here! (bug 329026)returnNS_OK;mThebes->SetLineJoin(j);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetLineJoin(nsAString&joinstyle){if(!EnsureSurface())returnNS_ERROR_FAILURE;gfxContext::GraphicsLineJoinj=mThebes->CurrentLineJoin();if(j==gfxContext::LINE_JOIN_ROUND)joinstyle.AssignLiteral("round");elseif(j==gfxContext::LINE_JOIN_BEVEL)joinstyle.AssignLiteral("bevel");elseif(j==gfxContext::LINE_JOIN_MITER)joinstyle.AssignLiteral("miter");elsereturnNS_ERROR_FAILURE;returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetMiterLimit(floatmiter){if(!EnsureSurface())returnNS_ERROR_FAILURE;if(!FloatValidate(miter)||miter<=0.0)returnNS_OK;mThebes->SetMiterLimit(miter);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetMiterLimit(float*miter){if(!EnsureSurface())returnNS_ERROR_FAILURE;gfxFloatd=mThebes->CurrentMiterLimit();*miter=static_cast<float>(d);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetMozDash(JSContext*cx,constjsval&patternArray){if(!EnsureSurface())returnNS_ERROR_FAILURE;AutoFallibleTArray<gfxFloat,10>dashes;nsresultrv=JSValToDashArray(cx,patternArray,dashes);if(NS_SUCCEEDED(rv)){mThebes->SetDash(dashes.Elements(),dashes.Length(),mThebes->CurrentDashOffset());}returnrv;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetMozDash(JSContext*cx,jsval*dashArray){if(!EnsureSurface())returnNS_ERROR_FAILURE;AutoFallibleTArray<gfxFloat,10>dashes;if(!mThebes->CurrentDash(dashes,nsnull)){dashes.SetLength(0);}returnDashArrayToJSVal(dashes,cx,dashArray);}NS_IMETHODIMPnsCanvasRenderingContext2D::SetMozDashOffset(floatoffset){if(!EnsureSurface())returnNS_ERROR_FAILURE;if(!FloatValidate(offset)){returnNS_ERROR_ILLEGAL_VALUE;}AutoFallibleTArray<gfxFloat,10>dashes;if(!mThebes->CurrentDash(dashes,nsnull)){// Either no dash is set or the cairo call failed. Either// way, eat the error.// XXX ERRMSG we need to report an error to developers here! (bug 329026)returnNS_OK;}NS_ABORT_IF_FALSE(dashes.Length()>0,"CurrentDash() should have returned false");mThebes->SetDash(dashes.Elements(),dashes.Length(),gfxFloat(offset));returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetMozDashOffset(float*offset){if(!EnsureSurface())returnNS_ERROR_FAILURE;*offset=float(mThebes->CurrentDashOffset());returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::IsPointInPath(floatx,floaty,bool*retVal){if(!EnsureSurface())returnNS_ERROR_FAILURE;if(!FloatValidate(x,y)){*retVal=false;returnNS_OK;}gfxPointpt(x,y);*retVal=mThebes->PointInFill(mThebes->DeviceToUser(pt));returnNS_OK;}//// image//// drawImage(in HTMLImageElement image, in float dx, in float dy);// -- render image from 0,0 at dx,dy top-left coords// drawImage(in HTMLImageElement image, in float dx, in float dy, in float sw, in float sh);// -- render image from 0,0 at dx,dy top-left coords clipping it to sw,sh// 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);// -- render the region defined by (sx,sy,sw,wh) in image-local space into the region (dx,dy,dw,dh) on the canvasNS_IMETHODIMPnsCanvasRenderingContext2D::DrawImage(nsIDOMElement*imgElt,floata1,floata2,floata3,floata4,floata5,floata6,floata7,floata8,PRUint8optional_argc){if(!EnsureSurface())returnNS_ERROR_FAILURE;nsCOMPtr<nsIContent>content=do_QueryInterface(imgElt);if(!content){returnNS_ERROR_DOM_TYPE_MISMATCH_ERR;}nsHTMLCanvasElement*canvas=nsHTMLCanvasElement::FromContent(content);if(canvas){nsIntSizesize=canvas->GetSize();if(size.width==0||size.height==0){returnNS_ERROR_DOM_INVALID_STATE_ERR;}}gfxMatrixmatrix;nsRefPtr<gfxPattern>pattern;gfxIntSizeimgSize;nsRefPtr<gfxASurface>imgsurf=CanvasImageCache::Lookup(imgElt,HTMLCanvasElement(),&imgSize);if(!imgsurf){// The canvas spec says that drawImage should draw the first frame// of animated imagesPRUint32sfeFlags=nsLayoutUtils::SFE_WANT_FIRST_FRAME;nsLayoutUtils::SurfaceFromElementResultres=nsLayoutUtils::SurfaceFromElement(content->AsElement(),sfeFlags);if(!res.mSurface){// Spec says to silently do nothing if the element is still loading.returnres.mIsStillLoading?NS_OK:NS_ERROR_NOT_AVAILABLE;}// Force a copy if we're using drawImage with our destination// as a source to work around some Cairo self-copy semantics issues.if(res.mSurface==mSurface){sfeFlags|=nsLayoutUtils::SFE_WANT_NEW_SURFACE;res=nsLayoutUtils::SurfaceFromElement(content->AsElement(),sfeFlags);if(!res.mSurface)returnNS_ERROR_NOT_AVAILABLE;}imgsurf=res.mSurface.forget();imgSize=res.mSize;if(mCanvasElement){CanvasUtils::DoDrawImageSecurityCheck(HTMLCanvasElement(),res.mPrincipal,res.mIsWriteOnly,res.mCORSUsed);}if(res.mImageRequest){CanvasImageCache::NotifyDrawImage(imgElt,HTMLCanvasElement(),res.mImageRequest,imgsurf,imgSize);}}doublesx,sy,sw,sh;doubledx,dy,dw,dh;if(optional_argc==0){dx=a1;dy=a2;sx=sy=0.0;dw=sw=(double)imgSize.width;dh=sh=(double)imgSize.height;}elseif(optional_argc==2){dx=a1;dy=a2;dw=a3;dh=a4;sx=sy=0.0;sw=(double)imgSize.width;sh=(double)imgSize.height;}elseif(optional_argc==6){sx=a1;sy=a2;sw=a3;sh=a4;dx=a5;dy=a6;dw=a7;dh=a8;}else{// XXX ERRMSG we need to report an error to developers here! (bug 329026)returnNS_ERROR_INVALID_ARG;}if(dw==0.0||dh==0.0){// not really failure, but nothing to do --// and noone likes a divide-by-zeroreturnNS_OK;}if(!FloatValidate(sx,sy,sw,sh)||!FloatValidate(dx,dy,dw,dh)){returnNS_OK;}// check argsif(sx<0.0||sy<0.0||sw<0.0||sw>(double)imgSize.width||sh<0.0||sh>(double)imgSize.height||dw<0.0||dh<0.0){// XXX ERRMSG we need to report an error to developers here! (bug 329026)returnNS_ERROR_DOM_INDEX_SIZE_ERR;}matrix.Translate(gfxPoint(sx,sy));matrix.Scale(sw/dw,sh/dh);pattern=newgfxPattern(imgsurf);pattern->SetMatrix(matrix);pattern->SetExtend(gfxPattern::EXTEND_PAD);if(CurrentState().imageSmoothingEnabled)pattern->SetFilter(gfxPattern::FILTER_GOOD);elsepattern->SetFilter(gfxPattern::FILTER_NEAREST);PathAutoSaveRestorepathSR(this);// Clear the surface if we need to simulate unbounded SOURCE operatorClearSurfaceForUnboundedSource();{gfxContextMatrixAutoSaveRestoreautoMatrixSR(mThebes);mThebes->Translate(gfxPoint(dx,dy));gfxRectclip(0,0,dw,dh);if(NeedToDrawShadow()){gfxRectdrawExtents=mThebes->UserToDevice(clip);gfxAlphaBoxBlurblur;gfxContext*ctx=ShadowInitialize(drawExtents,blur);if(ctx){CopyContext(ctx,mThebes);ctx->SetPattern(pattern);ctx->SetOperator(gfxContext::OPERATOR_SOURCE);ctx->Clip(clip);ctx->Paint();ShadowFinalize(blur);}}mThebes->SetPattern(pattern);DirtyAllStyles();booldoUseIntermediateSurface=NeedToUseIntermediateSurface();if(doUseIntermediateSurface){gfxContextAutoSaveRestoreautoSR(mThebes);// draw onto a pushed groupmThebes->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);mThebes->Clip(clip);// don't want operators to be applied twicemThebes->SetOperator(gfxContext::OPERATOR_SOURCE);mThebes->Paint();mThebes->PopGroupToSource();mThebes->Paint(CurrentState().globalAlpha);}elseif(CurrentState().globalAlpha==1.0f&&mThebes->CurrentOperator()==gfxContext::OPERATOR_OVER){/* Direct2D isn't very good at clipping so use Fill() when we can */mThebes->NewPath();mThebes->Rectangle(clip);mThebes->Fill();}else{gfxContextAutoSaveRestoreautoSR(mThebes);/* we need to use to clip instead of fill for globalAlpha */mThebes->Clip(clip);mThebes->Paint(CurrentState().globalAlpha);}RedrawUser(clip);}returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetGlobalCompositeOperation(constnsAString&op){if(!EnsureSurface())returnNS_ERROR_FAILURE;gfxContext::GraphicsOperatorthebes_op;#define CANVAS_OP_TO_THEBES_OP(cvsop,thebesop) \ if (op.EqualsLiteral(cvsop)) \ thebes_op = gfxContext::OPERATOR_##thebesop;CANVAS_OP_TO_THEBES_OP("copy",SOURCE)elseCANVAS_OP_TO_THEBES_OP("destination-atop",DEST_ATOP)elseCANVAS_OP_TO_THEBES_OP("destination-in",DEST_IN)elseCANVAS_OP_TO_THEBES_OP("destination-out",DEST_OUT)elseCANVAS_OP_TO_THEBES_OP("destination-over",DEST_OVER)elseCANVAS_OP_TO_THEBES_OP("lighter",ADD)elseCANVAS_OP_TO_THEBES_OP("source-atop",ATOP)elseCANVAS_OP_TO_THEBES_OP("source-in",IN)elseCANVAS_OP_TO_THEBES_OP("source-out",OUT)elseCANVAS_OP_TO_THEBES_OP("source-over",OVER)elseCANVAS_OP_TO_THEBES_OP("xor",XOR)// XXX ERRMSG we need to report an error to developers here! (bug 329026)elsereturnNS_OK;#undef CANVAS_OP_TO_THEBES_OPmThebes->SetOperator(thebes_op);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetGlobalCompositeOperation(nsAString&op){if(!EnsureSurface())returnNS_ERROR_FAILURE;gfxContext::GraphicsOperatorthebes_op=mThebes->CurrentOperator();#define CANVAS_OP_TO_THEBES_OP(cvsop,thebesop) \ if (thebes_op == gfxContext::OPERATOR_##thebesop) \ op.AssignLiteral(cvsop);CANVAS_OP_TO_THEBES_OP("copy",SOURCE)elseCANVAS_OP_TO_THEBES_OP("destination-atop",DEST_ATOP)elseCANVAS_OP_TO_THEBES_OP("destination-in",DEST_IN)elseCANVAS_OP_TO_THEBES_OP("destination-out",DEST_OUT)elseCANVAS_OP_TO_THEBES_OP("destination-over",DEST_OVER)elseCANVAS_OP_TO_THEBES_OP("lighter",ADD)elseCANVAS_OP_TO_THEBES_OP("source-atop",ATOP)elseCANVAS_OP_TO_THEBES_OP("source-in",IN)elseCANVAS_OP_TO_THEBES_OP("source-out",OUT)elseCANVAS_OP_TO_THEBES_OP("source-over",OVER)elseCANVAS_OP_TO_THEBES_OP("xor",XOR)elsereturnNS_ERROR_FAILURE;#undef CANVAS_OP_TO_THEBES_OPreturnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::DrawWindow(nsIDOMWindow*aWindow,floataX,floataY,floataW,floataH,constnsAString&aBGColor,PRUint32flags){if(!EnsureSurface())returnNS_ERROR_FAILURE;NS_ENSURE_ARG(aWindow!=nsnull);// protect against too-large surfaces that will cause allocation// or overflow issuesif(!gfxASurface::CheckSurfaceSize(gfxIntSize(PRInt32(aW),PRInt32(aH)),0xffff))returnNS_ERROR_FAILURE;// We can't allow web apps to call this until we fix at least the// following potential security issues:// -- rendering cross-domain IFRAMEs and then extracting the results// -- rendering the user's theme and then extracting the results// -- rendering native anonymous content (e.g., file input paths;// scrollbars should be allowed)if(!nsContentUtils::IsCallerTrustedForRead()){// not permitted to use DrawWindow// XXX ERRMSG we need to report an error to developers here! (bug 329026)returnNS_ERROR_DOM_SECURITY_ERR;}// Flush layout updatesif(!(flags&nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH))nsContentUtils::FlushLayoutForTree(aWindow);nsRefPtr<nsPresContext>presContext;nsCOMPtr<nsPIDOMWindow>win=do_QueryInterface(aWindow);if(win){nsIDocShell*docshell=win->GetDocShell();if(docshell){docshell->GetPresContext(getter_AddRefs(presContext));}}if(!presContext)returnNS_ERROR_FAILURE;nscolorbgColor;nsIDocument*elementDoc=mCanvasElement?HTMLCanvasElement()->OwnerDoc():nsnull;// Pass the CSS Loader object to the parser, to allow parser error reports// to include the outer window ID.nsCSSParserparser(elementDoc?elementDoc->CSSLoader():nsnull);nsresultrv=parser.ParseColorString(PromiseFlatString(aBGColor),nsnull,0,&bgColor);NS_ENSURE_SUCCESS(rv,rv);nsIPresShell*presShell=presContext->PresShell();NS_ENSURE_TRUE(presShell,NS_ERROR_FAILURE);nsRectr(nsPresContext::CSSPixelsToAppUnits(aX),nsPresContext::CSSPixelsToAppUnits(aY),nsPresContext::CSSPixelsToAppUnits(aW),nsPresContext::CSSPixelsToAppUnits(aH));PRUint32renderDocFlags=(nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING|nsIPresShell::RENDER_DOCUMENT_RELATIVE);if(flags&nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_CARET){renderDocFlags|=nsIPresShell::RENDER_CARET;}if(flags&nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_VIEW){renderDocFlags&=~(nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING|nsIPresShell::RENDER_DOCUMENT_RELATIVE);}if(flags&nsIDOMCanvasRenderingContext2D::DRAWWINDOW_USE_WIDGET_LAYERS){renderDocFlags|=nsIPresShell::RENDER_USE_WIDGET_LAYERS;}if(flags&nsIDOMCanvasRenderingContext2D::DRAWWINDOW_ASYNC_DECODE_IMAGES){renderDocFlags|=nsIPresShell::RENDER_ASYNC_DECODE_IMAGES;}rv=presShell->RenderDocument(r,renderDocFlags,bgColor,mThebes);// get rid of the pattern surface ref, just in casemThebes->SetColor(gfxRGBA(1,1,1,1));DirtyAllStyles();// note that aX and aY are coordinates in the document that// we're drawing; aX and aY are drawn to 0,0 in current user// space.RedrawUser(gfxRect(0,0,aW,aH));returnrv;}NS_IMETHODIMPnsCanvasRenderingContext2D::AsyncDrawXULElement(nsIDOMXULElement*aElem,floataX,floataY,floataW,floataH,constnsAString&aBGColor,PRUint32flags){if(!EnsureSurface())returnNS_ERROR_FAILURE;NS_ENSURE_ARG(aElem!=nsnull);// We can't allow web apps to call this until we fix at least the// following potential security issues:// -- rendering cross-domain IFRAMEs and then extracting the results// -- rendering the user's theme and then extracting the results// -- rendering native anonymous content (e.g., file input paths;// scrollbars should be allowed)if(!nsContentUtils::IsCallerTrustedForRead()){// not permitted to use DrawWindow// XXX ERRMSG we need to report an error to developers here! (bug 329026)returnNS_ERROR_DOM_SECURITY_ERR;}nsCOMPtr<nsIFrameLoaderOwner>loaderOwner=do_QueryInterface(aElem);if(!loaderOwner)returnNS_ERROR_FAILURE;nsRefPtr<nsFrameLoader>frameloader=loaderOwner->GetFrameLoader();if(!frameloader)returnNS_ERROR_FAILURE;PBrowserParent*child=frameloader->GetRemoteBrowser();if(!child){nsCOMPtr<nsIDOMWindow>window=do_GetInterface(frameloader->GetExistingDocShell());if(!window)returnNS_ERROR_FAILURE;returnDrawWindow(window,aX,aY,aW,aH,aBGColor,flags);}// protect against too-large surfaces that will cause allocation// or overflow issuesif(!gfxASurface::CheckSurfaceSize(gfxIntSize(aW,aH),0xffff))returnNS_ERROR_FAILURE;boolflush=(flags&nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH)==0;PRUint32renderDocFlags=nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;if(flags&nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_CARET){renderDocFlags|=nsIPresShell::RENDER_CARET;}if(flags&nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_VIEW){renderDocFlags&=~nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;}nsRectrect(nsPresContext::CSSPixelsToAppUnits(aX),nsPresContext::CSSPixelsToAppUnits(aY),nsPresContext::CSSPixelsToAppUnits(aW),nsPresContext::CSSPixelsToAppUnits(aH));if(mIPC){PDocumentRendererParent*pdocrender=child->SendPDocumentRendererConstructor(rect,mThebes->CurrentMatrix(),nsString(aBGColor),renderDocFlags,flush,nsIntSize(mWidth,mHeight));if(!pdocrender)returnNS_ERROR_FAILURE;DocumentRendererParent*docrender=static_cast<DocumentRendererParent*>(pdocrender);docrender->SetCanvasContext(this,mThebes);}returnNS_OK;}//// device pixel getting/setting//voidnsCanvasRenderingContext2D::EnsureUnpremultiplyTable(){if(sUnpremultiplyTable)return;// Infallably alloc the unpremultiply table.sUnpremultiplyTable=newPRUint8[256][256];// It's important that the array be indexed first by alpha and then by rgb// value. When we unpremultiply a pixel, we're guaranteed to do three// lookups with the same alpha; indexing by alpha first makes it likely that// those three lookups will be close to one another in memory, thus// increasing the chance of a cache hit.// a == 0 casefor(PRUint32c=0;c<=255;c++){sUnpremultiplyTable[0][c]=c;}for(inta=1;a<=255;a++){for(intc=0;c<=255;c++){sUnpremultiplyTable[a][c]=(PRUint8)((c*255)/a);}}}NS_IMETHODIMPnsCanvasRenderingContext2D::GetImageData(doubleaSx,doubleaSy,doubleaSw,doubleaSh,JSContext*aCx,nsIDOMImageData**aRetval){if(!mCanvasElement&&!mDocShell){NS_ERROR("No canvas element and no docshell in GetImageData!!!");returnNS_ERROR_DOM_SECURITY_ERR;}// Check only if we have a canvas element; if we were created with a docshell,// then it's special internal use.if(mCanvasElement&&HTMLCanvasElement()->IsWriteOnly()&&!nsContentUtils::IsCallerTrustedForRead()){// XXX ERRMSG we need to report an error to developers here! (bug 329026)returnNS_ERROR_DOM_SECURITY_ERR;}if(!EnsureSurface()){returnNS_ERROR_FAILURE;}if(!NS_finite(aSx)||!NS_finite(aSy)||!NS_finite(aSw)||!NS_finite(aSh)){returnNS_ERROR_DOM_NOT_SUPPORTED_ERR;}if(!aSw||!aSh){returnNS_ERROR_DOM_INDEX_SIZE_ERR;}int32_tx=JS_DoubleToInt32(aSx);int32_ty=JS_DoubleToInt32(aSy);int32_twi=JS_DoubleToInt32(aSw);int32_thi=JS_DoubleToInt32(aSh);// Handle negative width and height by flipping the rectangle over in the// relevant direction.uint32_tw,h;if(aSw<0){w=-wi;x-=w;}else{w=wi;}if(aSh<0){h=-hi;y-=h;}else{h=hi;}if(w==0){w=1;}if(h==0){h=1;}JSObject*array;nsresultrv=GetImageDataArray(aCx,x,y,w,h,&array);NS_ENSURE_SUCCESS(rv,rv);MOZ_ASSERT(array);nsRefPtr<ImageData>imageData=newImageData(w,h,*array);imageData.forget(aRetval);returnNS_OK;}nsresultnsCanvasRenderingContext2D::GetImageDataArray(JSContext*aCx,int32_taX,int32_taY,uint32_taWidth,uint32_taHeight,JSObject**aRetval){MOZ_ASSERT(aWidth&&aHeight);CheckedInt<uint32_t>len=CheckedInt<uint32_t>(aWidth)*aHeight*4;if(!len.isValid()){returnNS_ERROR_DOM_INDEX_SIZE_ERR;}CheckedInt<int32_t>rightMost=CheckedInt<int32_t>(aX)+aWidth;CheckedInt<int32_t>bottomMost=CheckedInt<int32_t>(aY)+aHeight;if(!rightMost.isValid()||!bottomMost.isValid()){returnNS_ERROR_DOM_SYNTAX_ERR;}JSObject*darray=JS_NewUint8ClampedArray(aCx,len.value());if(!darray){returnNS_ERROR_OUT_OF_MEMORY;}uint8_t*data=JS_GetUint8ClampedArrayData(darray,aCx);/* Copy the surface contents to the buffer */nsRefPtr<gfxImageSurface>tmpsurf=newgfxImageSurface(data,gfxIntSize(aWidth,aHeight),aWidth*4,gfxASurface::ImageFormatARGB32);if(tmpsurf->CairoStatus())returnNS_ERROR_FAILURE;nsRefPtr<gfxContext>tmpctx=newgfxContext(tmpsurf);if(tmpctx->HasError())returnNS_ERROR_FAILURE;if(!mZero){gfxRectsrcRect(0,0,mWidth,mHeight);gfxRectdestRect(aX,aY,aWidth,aHeight);boolfinishedPainting=false;// In the common case, we want to avoid the Rectangle call.if(!srcRect.Contains(destRect)){// If the requested area is entirely outside the canvas, we're done.gfxRecttmp=srcRect.Intersect(destRect);finishedPainting=tmp.IsEmpty();// Set clipping region if necessary.if(!finishedPainting){tmpctx->Rectangle(tmp);}}if(!finishedPainting){tmpctx->SetOperator(gfxContext::OPERATOR_SOURCE);tmpctx->SetSource(mSurface,gfxPoint(-aX,-aY));tmpctx->Paint();}}// make sure sUnpremultiplyTable has been createdEnsureUnpremultiplyTable();// NOTE! dst is the same as src, and this relies on reading// from src and advancing that ptr before writing to dst.uint8_t*src=data;uint8_t*dst=data;for(uint32_tj=0;j<aHeight;++j){for(uint32_ti=0;i<aWidth;++i){// XXX Is there some useful swizzle MMX we can use here?#ifdef IS_LITTLE_ENDIANPRUint8b=*src++;PRUint8g=*src++;PRUint8r=*src++;PRUint8a=*src++;#elsePRUint8a=*src++;PRUint8r=*src++;PRUint8g=*src++;PRUint8b=*src++;#endif// Convert to non-premultiplied color*dst++=sUnpremultiplyTable[a][r];*dst++=sUnpremultiplyTable[a][g];*dst++=sUnpremultiplyTable[a][b];*dst++=a;}}*aRetval=darray;returnNS_OK;}voidnsCanvasRenderingContext2D::EnsurePremultiplyTable(){if(sPremultiplyTable)return;// Infallably alloc the premultiply table.sPremultiplyTable=newPRUint8[256][256];// Like the unpremultiply table, it's important that we index the premultiply// table with the alpha value as the first index to ensure good cache// performance.for(inta=0;a<=255;a++){for(intc=0;c<=255;c++){sPremultiplyTable[a][c]=(a*c+254)/255;}}}// void putImageData (in ImageData d, in float x, in float y);NS_IMETHODIMPnsCanvasRenderingContext2D::PutImageData(){/* Should never be called -- PutImageData_explicit is the QS entry point */returnNS_ERROR_NOT_IMPLEMENTED;}NS_IMETHODIMPnsCanvasRenderingContext2D::PutImageData_explicit(PRInt32x,PRInt32y,PRUint32w,PRUint32h,unsignedchar*aData,PRUint32aDataLen,boolhasDirtyRect,PRInt32dirtyX,PRInt32dirtyY,PRInt32dirtyWidth,PRInt32dirtyHeight){if(!EnsureSurface())returnNS_ERROR_FAILURE;if(w==0||h==0)returnNS_ERROR_DOM_SYNTAX_ERR;gfxRectdirtyRect;gfxRectimageDataRect(0,0,w,h);if(hasDirtyRect){// fix up negative dimensionsif(dirtyWidth<0){NS_ENSURE_TRUE(dirtyWidth!=INT_MIN,NS_ERROR_DOM_INDEX_SIZE_ERR);CheckedInt32checkedDirtyX=CheckedInt32(dirtyX)+dirtyWidth;if(!checkedDirtyX.isValid())returnNS_ERROR_DOM_INDEX_SIZE_ERR;dirtyX=checkedDirtyX.value();dirtyWidth=-(int32)dirtyWidth;}if(dirtyHeight<0){NS_ENSURE_TRUE(dirtyHeight!=INT_MIN,NS_ERROR_DOM_INDEX_SIZE_ERR);CheckedInt32checkedDirtyY=CheckedInt32(dirtyY)+dirtyHeight;if(!checkedDirtyY.isValid())returnNS_ERROR_DOM_INDEX_SIZE_ERR;dirtyY=checkedDirtyY.value();dirtyHeight=-(int32)dirtyHeight;}// bound the dirty rect within the imageData rectangledirtyRect=imageDataRect.Intersect(gfxRect(dirtyX,dirtyY,dirtyWidth,dirtyHeight));if(dirtyRect.Width()<=0||dirtyRect.Height()<=0)returnNS_OK;}else{dirtyRect=imageDataRect;}dirtyRect.MoveBy(gfxPoint(x,y));dirtyRect=gfxRect(0,0,mWidth,mHeight).Intersect(dirtyRect);if(dirtyRect.Width()<=0||dirtyRect.Height()<=0)returnNS_OK;PRUint32len=w*h*4;if(aDataLen!=len)returnNS_ERROR_DOM_SYNTAX_ERR;nsRefPtr<gfxImageSurface>imgsurf=newgfxImageSurface(gfxIntSize(w,h),gfxASurface::ImageFormatARGB32);if(!imgsurf||imgsurf->CairoStatus())returnNS_ERROR_FAILURE;// ensure premultiply table has been createdEnsurePremultiplyTable();PRUint8*src=aData;PRUint8*dst=imgsurf->Data();for(PRUint32j=0;j<h;j++){for(PRUint32i=0;i<w;i++){PRUint8r=*src++;PRUint8g=*src++;PRUint8b=*src++;PRUint8a=*src++;// Convert to premultiplied color (losslessly if the input came from getImageData)#ifdef IS_LITTLE_ENDIAN*dst++=sPremultiplyTable[a][b];*dst++=sPremultiplyTable[a][g];*dst++=sPremultiplyTable[a][r];*dst++=a;#else*dst++=a;*dst++=sPremultiplyTable[a][r];*dst++=sPremultiplyTable[a][g];*dst++=sPremultiplyTable[a][b];#endif}}PathAutoSaveRestorepathSR(this);gfxContextAutoSaveRestoreautoSR(mThebes);// ignore clipping region, as per specmThebes->ResetClip();mThebes->IdentityMatrix();mThebes->NewPath();mThebes->Rectangle(dirtyRect);mThebes->SetSource(imgsurf,gfxPoint(x,y));mThebes->SetOperator(gfxContext::OPERATOR_SOURCE);mThebes->Fill();returnRedraw(dirtyRect);}NS_IMETHODIMPnsCanvasRenderingContext2D::GetThebesSurface(gfxASurface**surface){if(!EnsureSurface()){*surface=nsnull;returnNS_ERROR_NOT_AVAILABLE;}*surface=mSurface.get();NS_ADDREF(*surface);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::CreateImageData(){/* Should never be called; handled entirely in the quickstub */returnNS_ERROR_NOT_IMPLEMENTED;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetMozImageSmoothingEnabled(bool*retVal){*retVal=CurrentState().imageSmoothingEnabled;returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetMozImageSmoothingEnabled(boolval){if(val!=CurrentState().imageSmoothingEnabled){CurrentState().imageSmoothingEnabled=val;DirtyAllStyles();}returnNS_OK;}staticPRUint8g2DContextLayerUserData;already_AddRefed<CanvasLayer>nsCanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder*aBuilder,CanvasLayer*aOldLayer,LayerManager*aManager){// If we don't have anything to draw, don't bother.if(!mValid||!mSurface||mSurface->CairoStatus()||!mThebes||!mSurfaceCreated){returnnsnull;}if(!mResetLayer&&aOldLayer){CanvasRenderingContext2DUserData*userData=static_cast<CanvasRenderingContext2DUserData*>(aOldLayer->GetUserData(&g2DContextLayerUserData));if(userData&&userData->IsForContext(this)){NS_ADDREF(aOldLayer);returnaOldLayer;}}nsRefPtr<CanvasLayer>canvasLayer=aManager->CreateCanvasLayer();if(!canvasLayer){NS_WARNING("CreateCanvasLayer returned null!");returnnsnull;}CanvasRenderingContext2DUserData*userData=nsnull;if(aBuilder->IsPaintingToWindow()){// Make the layer tell us whenever a transaction finishes (including// the current transaction), so we can clear our invalidation state and// start invalidating again. We need to do this for the layer that is// being painted to a window (there shouldn't be more than one at a time,// and if there is, flushing the invalidation state more often than// necessary is harmless).// The layer will be destroyed when we tear down the presentation// (at the latest), at which time this userData will be destroyed,// releasing the reference to the element.// The userData will receive DidTransactionCallbacks, which flush the// the invalidation state to indicate that the canvas is up to date.userData=newCanvasRenderingContext2DUserData(this);canvasLayer->SetDidTransactionCallback(CanvasRenderingContext2DUserData::DidTransactionCallback,userData);}canvasLayer->SetUserData(&g2DContextLayerUserData,userData);CanvasLayer::Datadata;data.mSurface=mSurface.get();data.mSize=nsIntSize(mWidth,mHeight);canvasLayer->Initialize(data);PRUint32flags=mOpaque?Layer::CONTENT_OPAQUE:0;canvasLayer->SetContentFlags(flags);canvasLayer->Updated();mResetLayer=false;returncanvasLayer.forget();}boolnsCanvasRenderingContext2D::ShouldForceInactiveLayer(LayerManager*aManager){return!aManager->CanUseCanvasLayerForSize(gfxIntSize(mWidth,mHeight));}voidnsCanvasRenderingContext2D::MarkContextClean(){if(mInvalidateCount>0){mPredictManyRedrawCalls=mInvalidateCount>kCanvasMaxInvalidateCount;}mIsEntireFrameInvalid=false;mInvalidateCount=0;}