/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- *//* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Vladimir Vukicevic <vladimir@pobox.com> * Portions created by the Initial Developer are Copyright (C) 2005 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Rob Arnold <tellrob@gmail.com> * Eric Butler <zantifon@gmail.com> * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */#ifdef _MSC_VER#define _USE_MATH_DEFINES#endif#include<math.h>#include"prmem.h"#include"nsIServiceManager.h"#include"nsContentUtils.h"#include"nsIDOMDocument.h"#include"nsIDocument.h"#include"nsIDOMCanvasRenderingContext2D.h"#include"nsICanvasRenderingContextInternal.h"#include"nsPresContext.h"#include"nsIPresShell.h"#include"nsIVariant.h"#include"nsIDOMHTMLCanvasElement.h"#include"nsICanvasElement.h"#include"nsIInterfaceRequestorUtils.h"#include"nsIFrame.h"#include"nsDOMError.h"#include"nsIScriptError.h"#include"nsICSSParser.h"#include"nsICSSStyleRule.h"#include"nsComputedDOMStyle.h"#include"nsStyleSet.h"#include"nsPrintfCString.h"#include"nsReadableUtils.h"#include"nsColor.h"#include"nsIRenderingContext.h"#include"nsIDeviceContext.h"#include"nsGfxCIID.h"#include"nsIScriptSecurityManager.h"#include"nsIDocShell.h"#include"nsPresContext.h"#include"nsIPresShell.h"#include"nsIDOMWindow.h"#include"nsPIDOMWindow.h"#include"nsIDocShell.h"#include"nsIDocShellTreeItem.h"#include"nsIDocShellTreeNode.h"#include"nsIXPConnect.h"#include"jsapi.h"#include"jsnum.h"#include"nsTArray.h"#include"imgIEncoder.h"#include"gfxContext.h"#include"gfxASurface.h"#include"gfxImageSurface.h"#include"gfxPlatform.h"#include"gfxFont.h"#include"gfxTextRunCache.h"#include"gfxBlur.h"#include"nsFrameManager.h"#include"nsBidiPresUtils.h"#include"CanvasUtils.h"usingnamespacemozilla;#ifndef M_PI#define M_PI 3.14159265358979323846#define M_PI_2 1.57079632679489661923#endif/* Float validation stuff */#define VALIDATE(_f) if (!JSDOUBLE_IS_FINITE(_f)) return PR_FALSE/* These must take doubles as args, because JSDOUBLE_IS_FINITE expects * to take the address of its argument; we can't cast/convert in the * macro. */staticPRBoolFloatValidate(doublef1){VALIDATE(f1);returnPR_TRUE;}staticPRBoolFloatValidate(doublef1,doublef2){VALIDATE(f1);VALIDATE(f2);returnPR_TRUE;}staticPRBoolFloatValidate(doublef1,doublef2,doublef3){VALIDATE(f1);VALIDATE(f2);VALIDATE(f3);returnPR_TRUE;}staticPRBoolFloatValidate(doublef1,doublef2,doublef3,doublef4){VALIDATE(f1);VALIDATE(f2);VALIDATE(f3);VALIDATE(f4);returnPR_TRUE;}staticPRBoolFloatValidate(doublef1,doublef2,doublef3,doublef4,doublef5){VALIDATE(f1);VALIDATE(f2);VALIDATE(f3);VALIDATE(f4);VALIDATE(f5);returnPR_TRUE;}staticPRBoolFloatValidate(doublef1,doublef2,doublef3,doublef4,doublef5,doublef6){VALIDATE(f1);VALIDATE(f2);VALIDATE(f3);VALIDATE(f4);VALIDATE(f5);VALIDATE(f6);returnPR_TRUE;}#undef VALIDATE/** ** nsCanvasGradient **/#define NS_CANVASGRADIENT_PRIVATE_IID \ { 0x491d39d8, 0x4058, 0x42bd, { 0xac, 0x76, 0x70, 0xd5, 0x62, 0x7f, 0x02, 0x10 } }classnsCanvasGradient:publicnsIDOMCanvasGradient{public:NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASGRADIENT_PRIVATE_IID)nsCanvasGradient(gfxPattern*pat,nsICSSParser*cssparser):mPattern(pat),mCSSParser(cssparser){}gfxPattern*GetPattern(){returnmPattern;}/* nsIDOMCanvasGradient */NS_IMETHODAddColorStop(floatoffset,constnsAString&colorstr){nscolorcolor;if(!FloatValidate(offset))returnNS_ERROR_DOM_SYNTAX_ERR;if(offset<0.0||offset>1.0)returnNS_ERROR_DOM_INDEX_SIZE_ERR;nsresultrv=mCSSParser->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;nsCOMPtr<nsICSSParser>mCSSParser;};NS_DEFINE_STATIC_IID_ACCESSOR(nsCanvasGradient,NS_CANVASGRADIENT_PRIVATE_IID)NS_IMPL_ADDREF(nsCanvasGradient)NS_IMPL_RELEASE(nsCanvasGradient)NS_INTERFACE_MAP_BEGIN(nsCanvasGradient)NS_INTERFACE_MAP_ENTRY(nsCanvasGradient)NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasGradient)NS_INTERFACE_MAP_ENTRY_CONTENT_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 } }classnsCanvasPattern:publicnsIDOMCanvasPattern{public:NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASPATTERN_PRIVATE_IID)nsCanvasPattern(gfxPattern*pat,nsIPrincipal*principalForSecurityCheck,PRBoolforceWriteOnly):mPattern(pat),mPrincipal(principalForSecurityCheck),mForceWriteOnly(forceWriteOnly){}gfxPattern*GetPattern(){returnmPattern;}nsIPrincipal*Principal(){returnmPrincipal;}PRBoolGetForceWriteOnly(){returnmForceWriteOnly;}NS_DECL_ISUPPORTSprotected:nsRefPtr<gfxPattern>mPattern;nsCOMPtr<nsIPrincipal>mPrincipal;PRPackedBoolmForceWriteOnly;};NS_DEFINE_STATIC_IID_ACCESSOR(nsCanvasPattern,NS_CANVASPATTERN_PRIVATE_IID)NS_IMPL_ADDREF(nsCanvasPattern)NS_IMPL_RELEASE(nsCanvasPattern)NS_INTERFACE_MAP_BEGIN(nsCanvasPattern)NS_INTERFACE_MAP_ENTRY(nsCanvasPattern)NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasPattern)NS_INTERFACE_MAP_ENTRY_CONTENT_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)NS_INTERFACE_MAP_BEGIN(nsTextMetrics)NS_INTERFACE_MAP_ENTRY(nsTextMetrics)NS_INTERFACE_MAP_ENTRY(nsIDOMTextMetrics)NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(TextMetrics)NS_INTERFACE_MAP_ENTRY(nsISupports)NS_INTERFACE_MAP_ENDstructnsCanvasBidiProcessor;/** ** nsCanvasRenderingContext2D **/classnsCanvasRenderingContext2D:publicnsIDOMCanvasRenderingContext2D,publicnsICanvasRenderingContextInternal{public:nsCanvasRenderingContext2D();virtual~nsCanvasRenderingContext2D();nsresultRedraw();// this rect is in CSS pixelsnsresultRedraw(constgfxRect&r);// nsICanvasRenderingContextInternalNS_IMETHODSetCanvasElement(nsICanvasElement*aParentCanvas);NS_IMETHODSetDimensions(PRInt32width,PRInt32height);NS_IMETHODInitializeWithSurface(nsIDocShell*shell,gfxASurface*surface,PRInt32width,PRInt32height);NS_IMETHODRender(gfxContext*ctx,gfxPattern::GraphicsFilteraFilter);NS_IMETHODGetInputStream(constchar*aMimeType,constPRUnichar*aEncoderOptions,nsIInputStream**aStream);NS_IMETHODGetThebesSurface(gfxASurface**surface);NS_IMETHODSetIsOpaque(PRBoolisOpaque);// nsISupports interfaceNS_DECL_ISUPPORTS// nsIDOMCanvasRenderingContext2D interfaceNS_DECL_NSIDOMCANVASRENDERINGCONTEXT2DenumStyle{STYLE_STROKE=0,STYLE_FILL,STYLE_SHADOW,STYLE_MAX};protected:// destroy thebes/image stuff, in preparation for possibly recreatingvoidDestroy();// Some helpers. Doesn't modify acolor on failure.nsresultSetStyleFromVariant(nsIVariant*aStyle,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,PRBoolaUseGlobalAlpha=PR_TRUE);// Member varsPRInt32mWidth,mHeight;PRPackedBoolmValid;PRPackedBoolmOpaque;// the canvas element informs us when it's going away,// so these are not nsCOMPtrsnsICanvasElement*mCanvasElement;// If mCanvasElement is not provided, then a docshell isnsCOMPtr<nsIDocShell>mDocShell;// our CSS parser, for colors and whatnotnsCOMPtr<nsICSSParser>mCSSParser;// yay thebesnsRefPtr<gfxContext>mThebes;nsRefPtr<gfxASurface>mSurface;PRUint32mSaveCount;/** * Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever * Redraw is called, reset to false when Render is called. */PRBoolmIsEntireFrameInvalid;/** * 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. */PRBoolOperatorAffectsUncoveredAreas(gfxContext::GraphicsOperatorop)const{returnPR_FALSE;// XXX certain operators cause 2d.composite.uncovered.* tests to fail#if 0 return op == gfxContext::OPERATOR_IN || op == gfxContext::OPERATOR_OUT || op == gfxContext::OPERATOR_DEST_IN || op == gfxContext::OPERATOR_DEST_ATOP || op == gfxContext::OPERATOR_SOURCE;#endif}/** * Returns true iff a shadow should be drawn along with a * drawing operation. */PRBoolNeedToDrawShadow(){ContextState&state=CurrentState();// special case the default values as a "don't draw shadows" modePRBooldoDraw=state.colorStyles[STYLE_SHADOW]!=0||state.shadowOffset.x!=0||state.shadowOffset.y!=0;PRBoolisColor=CurrentState().StyleIsColor(STYLE_SHADOW);// if not using one of the cooky operators, can avoid drawing a shadow// if the color is fully transparentreturn(doDraw||!isColor)&&(!isColor||NS_GET_A(state.colorStyles[STYLE_SHADOW])!=0||OperatorAffectsUncoveredAreas(mThebes->CurrentOperator()));}/** * 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. */PRBoolNeedToUseIntermediateSurface(){// certain operators always need an intermediate surface, except// with quartz since quartz does compositing differently than cairoreturnmThebes->OriginalSurface()->GetType()!=gfxASurface::SurfaceTypeQuartz&&OperatorAffectsUncoveredAreas(mThebes->CurrentOperator());// XXX there are other unhandled cases but they should be investigated// first to ensure we aren't using an intermediate surface unecessarily}/** * Returns true iff the current source is such that global alpha would not * be handled correctly without the use of an intermediate surface. */PRBoolNeedIntermediateSurfaceToHandleGlobalAlpha(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 device-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_QueryInterface(mCanvasElement);if(content){nsIDocument*ownerDoc=content->GetOwnerDoc();returnownerDoc?ownerDoc->GetPrimaryShell():nsnull;}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();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;PRPackedBoolmDirtyStyle[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(PR_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. */inlinePRBoolStyleIsColor(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];PRPackedBoolimageSmoothingEnabled;};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;};NS_IMPL_ADDREF(nsCanvasRenderingContext2D)NS_IMPL_RELEASE(nsCanvasRenderingContext2D)NS_INTERFACE_MAP_BEGIN(nsCanvasRenderingContext2D)NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasRenderingContext2D)NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports,nsIDOMCanvasRenderingContext2D)NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(CanvasRenderingContext2D)NS_INTERFACE_MAP_END/** ** CanvasRenderingContext2D impl **/nsresultNS_NewCanvasRenderingContext2D(nsIDOMCanvasRenderingContext2D**aResult){nsRefPtr<nsIDOMCanvasRenderingContext2D>ctx=newnsCanvasRenderingContext2D();if(!ctx)returnNS_ERROR_OUT_OF_MEMORY;*aResult=ctx.forget().get();returnNS_OK;}nsCanvasRenderingContext2D::nsCanvasRenderingContext2D():mValid(PR_FALSE),mOpaque(PR_FALSE),mCanvasElement(nsnull),mSaveCount(0),mIsEntireFrameInvalid(PR_FALSE),mInvalidateCount(0),mLastStyle(STYLE_MAX),mStyleStack(20){}nsCanvasRenderingContext2D::~nsCanvasRenderingContext2D(){Destroy();}voidnsCanvasRenderingContext2D::Destroy(){mSurface=nsnull;mThebes=nsnull;mValid=PR_FALSE;mIsEntireFrameInvalid=PR_FALSE;}nsresultnsCanvasRenderingContext2D::SetStyleFromVariant(nsIVariant*aStyle,StyleaWhichStyle){nsresultrv;nscolorcolor;PRUint16paramType;rv=aStyle->GetDataType(&paramType);NS_ENSURE_SUCCESS(rv,rv);if(paramType==nsIDataType::VTYPE_DOMSTRING||paramType==nsIDataType::VTYPE_WSTRING_SIZE_IS){nsAutoStringstr;if(paramType==nsIDataType::VTYPE_DOMSTRING){rv=aStyle->GetAsDOMString(str);}else{rv=aStyle->GetAsAString(str);}NS_ENSURE_SUCCESS(rv,rv);rv=mCSSParser->ParseColorString(str,nsnull,0,&color);if(NS_FAILED(rv)){// Error reporting happens inside the CSS parserreturnNS_OK;}CurrentState().SetColorStyle(aWhichStyle,color);mDirtyStyle[aWhichStyle]=PR_TRUE;returnNS_OK;}elseif(paramType==nsIDataType::VTYPE_INTERFACE||paramType==nsIDataType::VTYPE_INTERFACE_IS){nsID*iid;nsCOMPtr<nsISupports>iface;rv=aStyle->GetAsInterface(&iid,getter_AddRefs(iface));nsCOMPtr<nsCanvasGradient>grad(do_QueryInterface(iface));if(grad){CurrentState().SetGradientStyle(aWhichStyle,grad);mDirtyStyle[aWhichStyle]=PR_TRUE;returnNS_OK;}nsCOMPtr<nsCanvasPattern>pattern(do_QueryInterface(iface));if(pattern){CurrentState().SetPatternStyle(aWhichStyle,pattern);mDirtyStyle[aWhichStyle]=PR_TRUE;returnNS_OK;}}nsContentUtils::ReportToConsole(nsContentUtils::eDOM_PROPERTIES,"UnexpectedCanvasVariantStyle",nsnull,0,nsnull,EmptyString(),0,0,nsIScriptError::warningFlag,"Canvas");returnNS_OK;}voidnsCanvasRenderingContext2D::StyleColorToString(constnscolor&aColor,nsAString&aStr){if(NS_GET_A(aColor)==255){CopyUTF8toUTF16(nsPrintfCString(100,"#%02x%02x%02x",NS_GET_R(aColor),NS_GET_G(aColor),NS_GET_B(aColor)),aStr);}else{// "%0.5f" in nsPrintfCString would use the locale-specific// decimal separator. That's why we have to do this:PRUint32alpha=NS_GET_A(aColor)*100000/255;CopyUTF8toUTF16(nsPrintfCString(100,"rgba(%d, %d, %d, 0.%d)",NS_GET_R(aColor),NS_GET_G(aColor),NS_GET_B(aColor),alpha),aStr);}}voidnsCanvasRenderingContext2D::DirtyAllStyles(){for(inti=0;i<STYLE_MAX;i++){mDirtyStyle[i]=PR_TRUE;}}voidnsCanvasRenderingContext2D::ApplyStyle(StyleaWhichStyle,PRBoolaUseGlobalAlpha){if(mLastStyle==aWhichStyle&&!mDirtyStyle[aWhichStyle]&&aUseGlobalAlpha){// nothing to do, this is already the set stylereturn;}// if not using global alpha, don't optimize with dirty bitif(aUseGlobalAlpha)mDirtyStyle[aWhichStyle]=PR_FALSE;mLastStyle=aWhichStyle;nsCanvasPattern*pattern=CurrentState().patternStyles[aWhichStyle];if(pattern){if(mCanvasElement)CanvasUtils::DoDrawImageSecurityCheck(mCanvasElement,pattern->Principal(),pattern->GetForceWriteOnly());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(!mCanvasElement)returnNS_OK;if(mIsEntireFrameInvalid)returnNS_OK;mIsEntireFrameInvalid=PR_TRUE;returnmCanvasElement->InvalidateFrame();}nsresultnsCanvasRenderingContext2D::Redraw(constgfxRect&r){if(!mCanvasElement)returnNS_OK;if(mIsEntireFrameInvalid)returnNS_OK;if(++mInvalidateCount>kCanvasMaxInvalidateCount)returnRedraw();returnmCanvasElement->InvalidateFrameSubrect(r);}NS_IMETHODIMPnsCanvasRenderingContext2D::SetDimensions(PRInt32width,PRInt32height){Destroy();nsRefPtr<gfxASurface>surface;// Check that the dimensions are saneif(gfxASurface::CheckSurfaceSize(gfxIntSize(width,height),0xffff)){gfxASurface::gfxImageFormatformat=gfxASurface::ImageFormatARGB32;if(mOpaque)format=gfxASurface::ImageFormatRGB24;surface=gfxPlatform::GetPlatform()->CreateOffscreenSurface(gfxIntSize(width,height),format);if(surface->CairoStatus()!=0){surface=NULL;}}returnInitializeWithSurface(NULL,surface,width,height);}NS_IMETHODIMPnsCanvasRenderingContext2D::InitializeWithSurface(nsIDocShell*docShell,gfxASurface*surface,PRInt32width,PRInt32height){Destroy();NS_ASSERTION(!docShell^!mCanvasElement,"Cannot set both docshell and canvas element");mDocShell=docShell;mWidth=width;mHeight=height;mSurface=surface;mThebes=surface?newgfxContext(mSurface):nsnull;/* Create dummy surfaces here */if(mSurface==nsnull||mSurface->CairoStatus()!=0||mThebes==nsnull||mThebes->HasError()){mSurface=newgfxImageSurface(gfxIntSize(1,1),gfxASurface::ImageFormatARGB32);mThebes=newgfxContext(mSurface);}else{mValid=PR_TRUE;}// set up our css parser, if necessaryif(!mCSSParser){mCSSParser=do_CreateInstance("@mozilla.org/content/css-parser;1");}// 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();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();// always force a redraw, because if the surface dimensions were reset// then the surface became cleared, and we need to redraw everything.Redraw();returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetIsOpaque(PRBoolisOpaque){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::Render(gfxContext*ctx,gfxPattern::GraphicsFilteraFilter){nsresultrv=NS_OK;if(!mValid||!mSurface||mSurface->CairoStatus()||mThebes->HasError())returnNS_ERROR_FAILURE;if(!mSurface)returnNS_ERROR_FAILURE;nsRefPtr<gfxPattern>pat=newgfxPattern(mSurface);pat->SetFilter(aFilter);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);mIsEntireFrameInvalid=PR_FALSE;mInvalidateCount=0;returnrv;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetInputStream(constchar*aMimeType,constPRUnichar*aEncoderOptions,nsIInputStream**aStream){if(!mValid||!mSurface||mSurface->CairoStatus()||mThebes->HasError())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);}//// nsCanvasRenderingContext2D impl//NS_IMETHODIMPnsCanvasRenderingContext2D::SetCanvasElement(nsICanvasElement*aCanvasElement){// don't hold a ref to this!mCanvasElement=aCanvasElement;returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetCanvas(nsIDOMHTMLCanvasElement**canvas){if(mCanvasElement==nsnull){*canvas=nsnull;returnNS_OK;}returnCallQueryInterface(mCanvasElement,canvas);}//// state//NS_IMETHODIMPnsCanvasRenderingContext2D::Save(){ContextStatestate=CurrentState();mStyleStack.AppendElement(state);mThebes->Save();mSaveCount++;returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::Restore(){if(mSaveCount==0)returnNS_OK;if(mSaveCount<0)returnNS_ERROR_DOM_INVALID_STATE_ERR;mStyleStack.RemoveElementAt(mSaveCount);mThebes->Restore();mLastStyle=STYLE_MAX;DirtyAllStyles();mSaveCount--;returnNS_OK;}//// transformations//NS_IMETHODIMPnsCanvasRenderingContext2D::Scale(floatx,floaty){if(!FloatValidate(x,y))returnNS_ERROR_DOM_SYNTAX_ERR;mThebes->Scale(x,y);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::Rotate(floatangle){if(!FloatValidate(angle))returnNS_ERROR_DOM_SYNTAX_ERR;mThebes->Rotate(angle);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::Translate(floatx,floaty){if(!FloatValidate(x,y))returnNS_ERROR_DOM_SYNTAX_ERR;mThebes->Translate(gfxPoint(x,y));returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::Transform(floatm11,floatm12,floatm21,floatm22,floatdx,floatdy){if(!FloatValidate(m11,m12,m21,m22,dx,dy))returnNS_ERROR_DOM_SYNTAX_ERR;gfxMatrixmatrix(m11,m12,m21,m22,dx,dy);mThebes->Multiply(matrix);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetTransform(floatm11,floatm12,floatm21,floatm22,floatdx,floatdy){if(!FloatValidate(m11,m12,m21,m22,dx,dy))returnNS_ERROR_DOM_SYNTAX_ERR;gfxMatrixmatrix(m11,m12,m21,m22,dx,dy);mThebes->SetMatrix(matrix);returnNS_OK;}//// colors//NS_IMETHODIMPnsCanvasRenderingContext2D::SetGlobalAlpha(floataGlobalAlpha){if(!FloatValidate(aGlobalAlpha))returnNS_ERROR_DOM_SYNTAX_ERR;// ignore invalid values, as per specif(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*aStyle){returnSetStyleFromVariant(aStyle,STYLE_STROKE);}NS_IMETHODIMPnsCanvasRenderingContext2D::GetStrokeStyle(nsIVariant**aStyle){nsresultrv;nsCOMPtr<nsIWritableVariant>var=do_CreateInstance("@mozilla.org/variant;1");if(!var)returnNS_ERROR_FAILURE;rv=var->SetWritable(PR_TRUE);NS_ENSURE_SUCCESS(rv,rv);if(CurrentState().patternStyles[STYLE_STROKE]){rv=var->SetAsISupports(CurrentState().patternStyles[STYLE_STROKE]);NS_ENSURE_SUCCESS(rv,rv);}elseif(CurrentState().gradientStyles[STYLE_STROKE]){rv=var->SetAsISupports(CurrentState().gradientStyles[STYLE_STROKE]);NS_ENSURE_SUCCESS(rv,rv);}else{nsStringstyleStr;StyleColorToString(CurrentState().colorStyles[STYLE_STROKE],styleStr);rv=var->SetAsDOMString(styleStr);NS_ENSURE_SUCCESS(rv,rv);}*aStyle=var.forget().get();returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetFillStyle(nsIVariant*aStyle){returnSetStyleFromVariant(aStyle,STYLE_FILL);}NS_IMETHODIMPnsCanvasRenderingContext2D::GetFillStyle(nsIVariant**aStyle){nsresultrv;nsCOMPtr<nsIWritableVariant>var=do_CreateInstance("@mozilla.org/variant;1");if(!var)returnNS_ERROR_FAILURE;rv=var->SetWritable(PR_TRUE);NS_ENSURE_SUCCESS(rv,rv);if(CurrentState().patternStyles[STYLE_FILL]){rv=var->SetAsISupports(CurrentState().patternStyles[STYLE_FILL]);NS_ENSURE_SUCCESS(rv,rv);}elseif(CurrentState().gradientStyles[STYLE_FILL]){rv=var->SetAsISupports(CurrentState().gradientStyles[STYLE_FILL]);NS_ENSURE_SUCCESS(rv,rv);}else{nsStringstyleStr;StyleColorToString(CurrentState().colorStyles[STYLE_FILL],styleStr);rv=var->SetAsDOMString(styleStr);NS_ENSURE_SUCCESS(rv,rv);}*aStyle=var.forget().get();returnNS_OK;}//// gradients and patterns//NS_IMETHODIMPnsCanvasRenderingContext2D::CreateLinearGradient(floatx0,floaty0,floatx1,floaty1,nsIDOMCanvasGradient**_retval){if(!FloatValidate(x0,y0,x1,y1))returnNS_ERROR_DOM_SYNTAX_ERR;nsRefPtr<gfxPattern>gradpat=newgfxPattern(x0,y0,x1,y1);if(!gradpat)returnNS_ERROR_OUT_OF_MEMORY;nsRefPtr<nsIDOMCanvasGradient>grad=newnsCanvasGradient(gradpat,mCSSParser);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_SYNTAX_ERR;nsRefPtr<gfxPattern>gradpat=newgfxPattern(x0,y0,r0,x1,y1,r1);if(!gradpat)returnNS_ERROR_OUT_OF_MEMORY;nsRefPtr<nsIDOMCanvasGradient>grad=newnsCanvasGradient(gradpat,mCSSParser);if(!grad)returnNS_ERROR_OUT_OF_MEMORY;*_retval=grad.forget().get();returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::CreatePattern(nsIDOMHTMLElement*image,constnsAString&repeat,nsIDOMCanvasPattern**_retval){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;}// The canvas spec says that createPattern should use the first frame// of animated imagesnsLayoutUtils::SurfaceFromElementResultres=nsLayoutUtils::SurfaceFromElement(image,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);if(!pat)returnNS_ERROR_OUT_OF_MEMORY;*_retval=pat.forget().get();returnNS_OK;}//// shadows//NS_IMETHODIMPnsCanvasRenderingContext2D::SetShadowOffsetX(floatx){if(!FloatValidate(x))returnNS_ERROR_DOM_SYNTAX_ERR;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_ERROR_DOM_SYNTAX_ERR;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))returnNS_ERROR_DOM_SYNTAX_ERR;if(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){nscolorcolor;nsresultrv=mCSSParser->ParseColorString(nsString(colorstr),nsnull,0,&color);if(NS_FAILED(rv)){// Error reporting happens inside the CSS parserreturnNS_OK;}CurrentState().SetColorStyle(STYLE_SHADOW,color);mDirtyStyle[STYLE_SHADOW]=PR_TRUE;returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetShadowColor(nsAString&color){StyleColorToString(CurrentState().colorStyles[STYLE_SHADOW],color);returnNS_OK;}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());}staticconstgfxFloatSIGMA_MAX=25;gfxContext*nsCanvasRenderingContext2D::ShadowInitialize(constgfxRect&extents,gfxAlphaBoxBlur&blur){gfxIntSizeblurRadius;gfxFloatsigma=CurrentState().shadowBlur>8?sqrt(CurrentState().shadowBlur):CurrentState().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.Outset(blurRadius.height,blurRadius.width,blurRadius.height,blurRadius.width);drawExtents=drawExtents.Intersect(clipExtents-CurrentState().shadowOffset);gfxContext*ctx=blur.Init(drawExtents,blurRadius,nsnull);if(!ctx)returnnsnull;returnctx;}voidnsCanvasRenderingContext2D::ShadowFinalize(gfxAlphaBoxBlur&blur){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){/* * Need an intermediate surface when: * - globalAlpha != 1 and gradients/patterns are used (need to paint_with_alpha) * - certain operators are used and are not on mac (quartz/cairo composite operators don't quite line up) */PRBooldoUseIntermediateSurface=NeedToUseIntermediateSurface()||NeedIntermediateSurfaceToHandleGlobalAlpha(style);PRBooldoDrawShadow=NeedToDrawShadow();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,PR_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 twicemThebes->SetOperator(gfxContext::OPERATOR_SOURCE);}ApplyStyle(style);if(style==STYLE_FILL)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();}*dirtyRect=mThebes->UserToDevice(*dirtyRect);}returnNS_OK;}//// rects//NS_IMETHODIMPnsCanvasRenderingContext2D::ClearRect(floatx,floaty,floatw,floath){if(!FloatValidate(x,y,w,h))returnNS_ERROR_DOM_SYNTAX_ERR;gfxContextPathAutoSaveRestorepathSR(mThebes);gfxContextAutoSaveRestoreautoSR(mThebes);mThebes->SetOperator(gfxContext::OPERATOR_CLEAR);mThebes->NewPath();mThebes->Rectangle(gfxRect(x,y,w,h));mThebes->Fill();gfxRectdirty=mThebes->UserToDevice(mThebes->GetUserPathExtent());returnRedraw(dirty);}nsresultnsCanvasRenderingContext2D::DrawRect(constgfxRect&rect,Stylestyle){if(!FloatValidate(rect.pos.x,rect.pos.y,rect.size.width,rect.size.height))returnNS_ERROR_DOM_SYNTAX_ERR;gfxContextPathAutoSaveRestorepathSR(mThebes);mThebes->NewPath();mThebes->Rectangle(rect);gfxRectdirty;nsresultrv=DrawPath(style,&dirty);if(NS_FAILED(rv))returnrv;returnRedraw(dirty);}NS_IMETHODIMPnsCanvasRenderingContext2D::FillRect(floatx,floaty,floatw,floath){returnDrawRect(gfxRect(x,y,w,h),STYLE_FILL);}NS_IMETHODIMPnsCanvasRenderingContext2D::StrokeRect(floatx,floaty,floatw,floath){returnDrawRect(gfxRect(x,y,w,h),STYLE_STROKE);}//// path bits//NS_IMETHODIMPnsCanvasRenderingContext2D::BeginPath(){mThebes->NewPath();returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::ClosePath(){mThebes->ClosePath();returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::Fill(){gfxRectdirty;nsresultrv=DrawPath(STYLE_FILL,&dirty);if(NS_FAILED(rv))returnrv;returnRedraw(dirty);}NS_IMETHODIMPnsCanvasRenderingContext2D::Stroke(){gfxRectdirty;nsresultrv=DrawPath(STYLE_STROKE,&dirty);if(NS_FAILED(rv))returnrv;returnRedraw(dirty);}NS_IMETHODIMPnsCanvasRenderingContext2D::Clip(){mThebes->Clip();returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::MoveTo(floatx,floaty){if(!FloatValidate(x,y))returnNS_ERROR_DOM_SYNTAX_ERR;mThebes->MoveTo(gfxPoint(x,y));returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::LineTo(floatx,floaty){if(!FloatValidate(x,y))returnNS_ERROR_DOM_SYNTAX_ERR;mThebes->LineTo(gfxPoint(x,y));returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::QuadraticCurveTo(floatcpx,floatcpy,floatx,floaty){if(!FloatValidate(cpx,cpy,x,y))returnNS_ERROR_DOM_SYNTAX_ERR;// we will always have a current point, since beginPath forces// a moveto(0,0)gfxPointc=mThebes->CurrentPoint();gfxPointp(x,y);gfxPointcp(cpx,cpy);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(!FloatValidate(cp1x,cp1y,cp2x,cp2y,x,y))returnNS_ERROR_DOM_SYNTAX_ERR;mThebes->CurveTo(gfxPoint(cp1x,cp1y),gfxPoint(cp2x,cp2y),gfxPoint(x,y));returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::ArcTo(floatx1,floaty1,floatx2,floaty2,floatradius){if(!FloatValidate(x1,y1,x2,y2,radius))returnNS_ERROR_DOM_SYNTAX_ERR;if(radius<0)returnNS_ERROR_DOM_INDEX_SIZE_ERR;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,intccw){if(!FloatValidate(x,y,r,startAngle,endAngle))returnNS_ERROR_DOM_SYNTAX_ERR;gfxPointp(x,y);if(ccw)mThebes->NegativeArc(p,r,startAngle,endAngle);elsemThebes->Arc(p,r,startAngle,endAngle);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::Rect(floatx,floaty,floatw,floath){if(!FloatValidate(x,y,w,h))returnNS_ERROR_DOM_SYNTAX_ERR;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 aCSSParser The CSS parser of the canvas rendering context * @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,nsICSSParser*aCSSParser,nsINode*aNode,nsICSSStyleRule**aResult){nsresultrv;nsCOMPtr<nsICSSStyleRule>rule;PRBoolchanged;nsIPrincipal*principal=aNode->NodePrincipal();nsIDocument*document=aNode->GetOwnerDoc();nsIURI*docURL=document->GetDocumentURI();nsIURI*baseURL=document->GetBaseURI();rv=aCSSParser->ParseStyleAttribute(EmptyString(),docURL,baseURL,principal,getter_AddRefs(rule));if(NS_FAILED(rv))returnrv;rv=aCSSParser->ParseProperty(eCSSProperty_font,aFont,docURL,baseURL,principal,rule->GetDeclaration(),&changed);if(NS_FAILED(rv))returnrv;// set line height to normal, as per specrv=aCSSParser->ParseProperty(eCSSProperty_line_height,NS_LITERAL_STRING("normal"),docURL,baseURL,principal,rule->GetDeclaration(),&changed);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();nsCStringlangGroup;presShell->GetPresContext()->GetLangGroup()->ToUTF8String(langGroup);nsCOMArray<nsIStyleRule>rules;nsCOMPtr<nsICSSStyleRule>rule;rv=CreateFontStyleRule(font,mCSSParser.get(),document,getter_AddRefs(rule));if(NS_FAILED(rv))returnrv;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::GetStyleContextForContent(content,nsnull,presShell);}else{// otherwise inherit from default (10px sans-serif)nsCOMPtr<nsICSSStyleRule>parentRule;rv=CreateFontStyleRule(NS_LITERAL_STRING("10px sans-serif"),mCSSParser.get(),document,getter_AddRefs(parentRule));if(NS_FAILED(rv))returnrv;nsCOMArray<nsIStyleRule>parentRules;parentRules.AppendObject(parentRule);parentContext=styleSet->ResolveStyleForRules(nsnull,nsnull,nsCSSPseudoElements::ePseudo_NotPseudoElement,nsnull,parentRules);}if(!parentContext)returnNS_ERROR_FAILURE;nsRefPtr<nsStyleContext>sc=styleSet->ResolveStyleForRules(parentContext,nsnull,nsCSSPseudoElements::ePseudo_NotPseudoElement,nsnull,rules);if(!sc)returnNS_ERROR_FAILURE;constnsStyleFont*fontStyle=sc->GetStyleFont();NS_ASSERTION(fontStyle,"Could not obtain font style");// 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 zoomconstnscoordfontSize=nsStyleFont::UnZoomText(parentContext->PresContext(),fontStyle->mFont.size);PRBoolprinterFont=(presShell->GetPresContext()->Type()==nsPresContext::eContext_PrintPreview||presShell->GetPresContext()->Type()==nsPresContext::eContext_Print);gfxFontStylestyle(fontStyle->mFont.style,fontStyle->mFont.weight,fontStyle->mFont.stretch,NSAppUnitsToFloatPixels(fontSize,aupcp),langGroup,fontStyle->mFont.sizeAdjust,fontStyle->mFont.systemFont,fontStyle->mFont.familyNameQuirks,printerFont);CurrentState().fontGroup=gfxPlatform::GetPlatform()->CreateFontGroup(fontStyle->mFont.name,&style,presShell->GetPresContext()->GetUserFontSet());NS_ASSERTION(CurrentState().fontGroup,"Could not get font group");CurrentState().font=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;// spec says to not throw error for invalid arg, but do it anywayelsereturnNS_ERROR_INVALID_ARG;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;default:NS_ERROR("textAlign holds invalid value");returnNS_ERROR_FAILURE;}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;// spec says to not throw error for invalid arg, but do it anywayelsereturnNS_ERROR_INVALID_ARG;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;default:NS_ERROR("textBaseline holds invalid value");returnNS_ERROR_FAILURE;}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){mTextRun=gfxTextRunCache::MakeTextRun(text,length,mFontgrp,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);}returnstatic_cast<nscoord>(textRunMetrics.mAdvanceWidth/gfxFloat(mAppUnitsPerDevPixel));}virtualvoidDrawText(nscoordxOffset,nscoordwidth){gfxPointpoint=mPt;point.x+=xOffset*mAppUnitsPerDevPixel;// offset is given in terms of left side of stringif(mTextRun->IsRightToLeft())point.x+=width*mAppUnitsPerDevPixel;// stroke or fill the text depending on operationif(mOp==nsCanvasRenderingContext2D::TEXT_DRAW_OPERATION_STROKE)mTextRun->DrawToPath(mThebes,point,0,mTextRun->GetLength(),nsnull,nsnull);else// mOp == TEXT_DRAW_OPERATION_FILLmTextRun->Draw(mThebes,point,0,mTextRun->GetLength(),nsnull,nsnull,nsnull);}// current text rungfxTextRunCache::AutoTextRunmTextRun;// 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 measuredPRBoolmDoMeasureBoundingBox;};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;}nsIPresShell*presShell=GetPresShell();if(!presShell)returnNS_ERROR_FAILURE;nsIDocument*document=presShell->GetDocument();nsBidiPresUtils*bidiUtils=presShell->GetPresContext()->GetBidiUtils();if(!bidiUtils)returnNS_ERROR_FAILURE;// replace all the whitespace characters with U+0020 SPACEnsAutoStringtextToDraw(aRawText);TextReplaceWhitespaceCharacters(textToDraw);// for now, default to ltr if not in docPRBoolisRTL=PR_FALSE;if(content&&content->IsInDoc()){// try to find the closest contextnsRefPtr<nsStyleContext>canvasStyle=nsComputedDOMStyle::GetStyleContextForContent(content,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;}// don't need to take care of these with stroke since Stroke() does thatPRBooldoDrawShadow=aOp==TEXT_DRAW_OPERATION_FILL&&NeedToDrawShadow();PRBooldoUseIntermediateSurface=aOp==TEXT_DRAW_OPERATION_FILL&&(NeedToUseIntermediateSurface()||NeedIntermediateSurfaceToHandleGlobalAlpha(STYLE_FILL));nsCanvasBidiProcessorprocessor;GetAppUnitsValues(&processor.mAppUnitsPerDevPixel,NULL);processor.mPt=gfxPoint(aX,aY);processor.mThebes=mThebes;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");nscoordtotalWidth;// calls bidi algo twice since it needs the full text width and the// bounding boxes before rendering anythingrv=bidiUtils->ProcessText(textToDraw.get(),textToDraw.Length(),isRTL?NSBIDI_RTL:NSBIDI_LTR,presShell->GetPresContext(),processor,nsBidiPresUtils::MODE_MEASURE,nsnull,0,&totalWidth);if(NS_FAILED(rv))returnrv;if(aWidth)*aWidth=static_cast<float>(totalWidth);// if only measuring, don't need to do any more workif(aOp==TEXT_DRAW_OPERATION_MEASURE)returnNS_OK;// 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 baselineNS_ASSERTION(processor.mFontgrp->FontListLength()>0,"font group contains no fonts");constgfxFont::Metrics&fontMetrics=processor.mFontgrp->GetFontAt(0)->GetMetrics();gfxFloatanchorY;switch(CurrentState().textBaseline){caseTEXT_BASELINE_TOP:anchorY=fontMetrics.emAscent;break;caseTEXT_BASELINE_HANGING:anchorY=0;// currently unavailablebreak;caseTEXT_BASELINE_MIDDLE:anchorY=(fontMetrics.emAscent-fontMetrics.emDescent)*.5f;break;caseTEXT_BASELINE_ALPHABETIC:anchorY=0;break;caseTEXT_BASELINE_IDEOGRAPHIC:anchorY=0;// currently unvailablebreak;caseTEXT_BASELINE_BOTTOM:anchorY=-fontMetrics.emDescent;break;default:NS_ERROR("mTextBaseline holds invalid value");returnNS_ERROR_FAILURE;}processor.mPt.y+=anchorY;// correct bounding box to get it to be the correct size/positionprocessor.mBoundingBox.size.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=PR_FALSE;if(doDrawShadow){// for some reason the box is too tight, probably rounding errorprocessor.mBoundingBox.Outset(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){CopyContext(ctx,mThebes);ctx->SetOperator(gfxContext::OPERATOR_SOURCE);processor.mThebes=ctx;rv=bidiUtils->ProcessText(textToDraw.get(),textToDraw.Length(),isRTL?NSBIDI_RTL:NSBIDI_LTR,presShell->GetPresContext(),processor,nsBidiPresUtils::MODE_DRAW,nsnull,0,nsnull);if(NS_FAILED(rv))returnrv;ShadowFinalize(blur);}processor.mThebes=mThebes;}gfxContextPathAutoSaveRestorepathSR(mThebes,PR_FALSE);// back up path if strokingif(aOp==nsCanvasRenderingContext2D::TEXT_DRAW_OPERATION_STROKE)pathSR.Save();// doUseIntermediateSurface is mutually exclusive to op == STROKEelse{if(doUseIntermediateSurface){mThebes->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);// don't want operators to be applied twicemThebes->SetOperator(gfxContext::OPERATOR_SOURCE);}ApplyStyle(STYLE_FILL);}rv=bidiUtils->ProcessText(textToDraw.get(),textToDraw.Length(),isRTL?NSBIDI_RTL:NSBIDI_LTR,presShell->GetPresContext(),processor,nsBidiPresUtils::MODE_DRAW,nsnull,0,nsnull);// this needs to be restored before function can returnif(doUseIntermediateSurface){mThebes->PopGroupToSource();DirtyAllStyles();}if(NS_FAILED(rv))returnrv;if(aOp==nsCanvasRenderingContext2D::TEXT_DRAW_OPERATION_STROKE){// DrawPath takes care of all shadows and composite odditiesrv=DrawPath(STYLE_STROKE);if(NS_FAILED(rv))returnrv;}elseif(doUseIntermediateSurface)mThebes->Paint(CurrentState().StyleIsColor(STYLE_FILL)?1.0:CurrentState().globalAlpha);if(aOp==nsCanvasRenderingContext2D::TEXT_DRAW_OPERATION_FILL&&!doDrawShadow)returnRedraw(mThebes->UserToDevice(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){#ifdef DEBUGnsresultres=#endifSetMozTextStyle(NS_LITERAL_STRING("10px sans-serif"));NS_ASSERTION(res==NS_OK,"Default canvas font is invalid");}returnCurrentState().fontGroup;}NS_IMETHODIMPnsCanvasRenderingContext2D::MozDrawText(constnsAString&textToDraw){constPRUnichar*textdata;textToDraw.GetData(&textdata);PRUint32textrunflags=0;PRUint32aupdp;GetAppUnitsValues(&aupdp,NULL);gfxTextRunCache::AutoTextRuntextRun;textRun=gfxTextRunCache::MakeTextRun(textdata,textToDraw.Length(),GetCurrentFontStyle(),mThebes,aupdp,textrunflags);if(!textRun.get())returnNS_ERROR_FAILURE;gfxPointpt(0.0f,0.0f);// Fill color is text colorApplyStyle(STYLE_FILL);textRun->Draw(mThebes,pt,/* offset = */0,textToDraw.Length(),nsnull,nsnull,nsnull);returnRedraw();}NS_IMETHODIMPnsCanvasRenderingContext2D::MozMeasureText(constnsAString&textToMeasure,float*retVal){nsCOMPtr<nsIDOMTextMetrics>metrics;nsresultrv;rv=MeasureText(textToMeasure,getter_AddRefs(metrics));if(NS_FAILED(rv))returnrv;returnmetrics->GetWidth(retVal);}NS_IMETHODIMPnsCanvasRenderingContext2D::MozPathText(constnsAString&textToPath){constPRUnichar*textdata;textToPath.GetData(&textdata);PRUint32textrunflags=0;PRUint32aupdp;GetAppUnitsValues(&aupdp,NULL);gfxTextRunCache::AutoTextRuntextRun;textRun=gfxTextRunCache::MakeTextRun(textdata,textToPath.Length(),GetCurrentFontStyle(),mThebes,aupdp,textrunflags);if(!textRun.get())returnNS_ERROR_FAILURE;gfxPointpt(0.0f,0.0f);textRun->DrawToPath(mThebes,pt,/* offset = */0,textToPath.Length(),nsnull,nsnull);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::MozTextAlongPath(constnsAString&textToDraw,PRBoolstroke){// Most of this code is copied from its svg equivalentnsRefPtr<gfxFlattenedPath>path(mThebes->GetFlattenedPath());constPRUnichar*textdata;textToDraw.GetData(&textdata);PRUint32textrunflags=0;PRUint32aupdp;GetAppUnitsValues(&aupdp,NULL);gfxTextRunCache::AutoTextRuntextRun;textRun=gfxTextRunCache::MakeTextRun(textdata,textToDraw.Length(),GetCurrentFontStyle(),mThebes,aupdp,textrunflags);if(!textRun.get())returnNS_ERROR_FAILURE;structPathChar{PRBooldraw;gfxFloatangle;gfxPointpos;PathChar():draw(PR_FALSE),angle(0.0),pos(0.0,0.0){}};gfxFloatlength=path->GetLength();PRUint32strLength=textToDraw.Length();PathChar*cp=newPathChar[strLength];if(!cp){returnNS_ERROR_OUT_OF_MEMORY;}gfxPointposition(0.0,0.0);gfxFloatx=position.x;for(PRUint32i=0;i<strLength;i++){gfxFloathalfAdvance=textRun->GetAdvanceWidth(i,1,nsnull)/(2.0*aupdp);// Check for end of pathif(x+halfAdvance>length)break;if(x+halfAdvance>=0){cp[i].draw=PR_TRUE;gfxPointpt=path->FindPoint(gfxPoint(x+halfAdvance,position.y),&(cp[i].angle));cp[i].pos=pt-gfxPoint(cos(cp[i].angle),sin(cp[i].angle))*halfAdvance;}x+=2*halfAdvance;}if(stroke){ApplyStyle(STYLE_STROKE);mThebes->NewPath();}else{ApplyStyle(STYLE_FILL);}for(PRUint32i=0;i<strLength;i++){// Skip non-visible charactersif(!cp[i].draw)continue;gfxMatrixmatrix=mThebes->CurrentMatrix();gfxMatrixrot;rot.Rotate(cp[i].angle);mThebes->Multiply(rot);rot.Invert();rot.Scale(aupdp,aupdp);gfxPointpt=rot.Transform(cp[i].pos);if(stroke){textRun->DrawToPath(mThebes,pt,i,1,nsnull,nsnull);}else{textRun->Draw(mThebes,pt,i,1,nsnull,nsnull,nsnull);}mThebes->SetMatrix(matrix);}if(stroke)mThebes->Stroke();delete[]cp;returnRedraw();}//// line caps/joins//NS_IMETHODIMPnsCanvasRenderingContext2D::SetLineWidth(floatwidth){if(!FloatValidate(width))returnNS_ERROR_DOM_SYNTAX_ERR;mThebes->SetLineWidth(width);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetLineWidth(float*width){gfxFloatd=mThebes->CurrentLineWidth();*width=static_cast<float>(d);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetLineCap(constnsAString&capstyle){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_ERROR_NOT_IMPLEMENTED;mThebes->SetLineCap(cap);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetLineCap(nsAString&capstyle){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){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_ERROR_NOT_IMPLEMENTED;mThebes->SetLineJoin(j);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetLineJoin(nsAString&joinstyle){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(!FloatValidate(miter))returnNS_ERROR_DOM_SYNTAX_ERR;mThebes->SetMiterLimit(miter);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetMiterLimit(float*miter){gfxFloatd=mThebes->CurrentMiterLimit();*miter=static_cast<float>(d);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::IsPointInPath(floatx,floaty,PRBool*retVal){if(!FloatValidate(x,y))returnNS_ERROR_DOM_SYNTAX_ERR;*retVal=mThebes->PointInFill(gfxPoint(x,y));returnNS_OK;}#ifdef WINCE/* A simple bitblt for self copies that ensures that we don't overwrite any * area before we've read from it. */staticvoidbitblt(gfxImageSurface*s,intsrc_x,intsrc_y,intwidth,intheight,intdest_x,intdest_y){unsignedchar*data=s->Data();intstride=s->Stride()/4;intx,y;unsignedint*dest=(unsignedint*)data;unsignedint*src=(unsignedint*)data;intsurface_width=s->Width();intsurface_height=s->Height();/* clip to the surface size */if(src_x<0){dest_x+=-src_x;width-=-src_x;src_x=0;}if(src_y<0){dest_y+=-src_y;height-=-src_y;src_y=0;}if(dest_x<0){src_x+=-dest_x;width-=-dest_x;dest_x=0;}if(dest_y<0){src_y+=-dest_y;height-=-dest_y;dest_y=0;}/*XXX: we might want to check for overflow? */if(src_x+width>surface_width)width=surface_width-src_x;if(dest_x+width>surface_width)width=surface_width-dest_x;if(src_y+height>surface_height)height=surface_height-src_y;if(dest_y+height>surface_height)height=surface_height-dest_y;if(dest_x<src_x){if(dest_y<src_y){dest=dest+dest_y*stride+dest_x;src=src+src_y*stride+src_x;/* copy right to left, top to bottom */for(y=0;y<height;y++){for(x=0;x<width;x++){*dest++=*src++;}dest+=stride-width;src+=stride-width;}}else{dest=dest+(dest_y+height-1)*stride+dest_x;src=src+(src_y+height-1)*stride+src_x;/* copy right to left, bottom to top */for(y=0;y<height;y++){for(x=0;x<width;x++){*dest++=*src++;}dest+=-stride-width;src+=-stride-width;}}}else{if(dest_y<src_y){dest=dest+dest_y*stride+(dest_x+width-1);src=src+src_y*stride+(src_x+width-1);/* copy left to right, top to bottom */for(y=0;y<height;y++){for(x=0;x<width;x++){*dest--=*src--;}dest+=stride+width;src+=stride+width;}}else{dest=dest+(dest_y+height-1)*stride+(dest_x+width-1);src=src+(src_y+height-1)*stride+(src_x+width-1);/* copy left to right, bottom to top */for(y=0;y<height;y++){for(x=0;x<width;x++){*dest--=*src--;}dest+=-stride+width;src+=-stride+width;}}}}#endif//// 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){NS_ENSURE_ARG(imgElt);nsresultrv;gfxRectdirty;doublesx,sy,sw,sh;doubledx,dy,dw,dh;gfxMatrixmatrix;nsRefPtr<gfxPattern>pattern;nsRefPtr<gfxPath>path;// The canvas spec says that drawImage should draw the first frame// of animated imagesPRUint32sfeFlags=nsLayoutUtils::SFE_WANT_FIRST_FRAME;nsLayoutUtils::SurfaceFromElementResultres=nsLayoutUtils::SurfaceFromElement(imgElt,sfeFlags);if(!res.mSurface)returnNS_ERROR_NOT_AVAILABLE;#ifndef WINCE// On non-CE, 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(imgElt,sfeFlags);if(!res.mSurface)returnNS_ERROR_NOT_AVAILABLE;}#endifnsRefPtr<gfxASurface>imgsurf=res.mSurface;nsCOMPtr<nsIPrincipal>principal=res.mPrincipal;gfxIntSizeimgSize=res.mSize;PRBoolforceWriteOnly=res.mIsWriteOnly;if(mCanvasElement)CanvasUtils::DoDrawImageSecurityCheck(mCanvasElement,principal,forceWriteOnly);gfxContextPathAutoSaveRestorepathSR(mThebes,PR_FALSE);rv=NS_OK;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)rv=NS_ERROR_INVALID_ARG;gotoFINISH;}if(dw==0.0||dh==0.0){rv=NS_OK;// not really failure, but nothing to do --// and noone likes a divide-by-zerogotoFINISH;}if(!FloatValidate(sx,sy,sw,sh)||!FloatValidate(dx,dy,dw,dh)){rv=NS_ERROR_DOM_SYNTAX_ERR;gotoFINISH;}// 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)rv=NS_ERROR_DOM_INDEX_SIZE_ERR;gotoFINISH;}matrix.Translate(gfxPoint(sx,sy));matrix.Scale(sw/dw,sh/dh);#ifdef WINCE/* cairo doesn't have consistent semantics for drawing a surface onto * itself. Specifically, pixman will not preserve the contents when doing * the copy. So to get the desired semantics a temporary copy would be needed. * Instead we optimize opaque self copies here */{nsRefPtr<gfxASurface>csurf=mThebes->CurrentSurface();if(csurf==imgsurf){if(imgsurf->GetType()==gfxASurface::SurfaceTypeImage){gfxImageSurface*surf=static_cast<gfxImageSurface*>(imgsurf.get());gfxContext::GraphicsOperatorop=mThebes->CurrentOperator();PRBoolopaque,unscaled;opaque=surf->Format()==gfxASurface::ImageFormatARGB32&&(op==gfxContext::OPERATOR_SOURCE);opaque|=surf->Format()==gfxASurface::ImageFormatRGB24&&(op==gfxContext::OPERATOR_SOURCE||op==gfxContext::OPERATOR_OVER);unscaled=sw==dw&&sh==dh;if(opaque&&unscaled){bitblt(surf,sx,sy,sw,sh,dx,dy);rv=NS_OK;gotoFINISH;}}}}#endifpattern=newgfxPattern(imgsurf);pattern->SetMatrix(matrix);if(CurrentState().imageSmoothingEnabled)pattern->SetFilter(gfxPattern::FILTER_GOOD);elsepattern->SetFilter(gfxPattern::FILTER_NEAREST);pathSR.Save();{gfxContextAutoSaveRestoreautoSR(mThebes);mThebes->Translate(gfxPoint(dx,dy));mThebes->SetPattern(pattern);gfxRectclip(0,0,dw,dh);if(NeedToDrawShadow()){gfxRectdrawExtents=mThebes->UserToDevice(clip);gfxAlphaBoxBlurblur;gfxContext*ctx=ShadowInitialize(drawExtents,blur);if(ctx){CopyContext(ctx,mThebes);ctx->SetOperator(gfxContext::OPERATOR_SOURCE);ctx->Clip(clip);ctx->Paint();ShadowFinalize(blur);}}PRBooldoUseIntermediateSurface=NeedToUseIntermediateSurface();mThebes->SetPattern(pattern);DirtyAllStyles();if(doUseIntermediateSurface){// 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();}elsemThebes->Clip(clip);dirty=mThebes->UserToDevice(clip);mThebes->Paint(CurrentState().globalAlpha);}#if 1// XXX cairo bug workaround; force a clip update on mThebes.// Otherwise, a pixman clip gets left around somewhere, and pixman// (Render) does source clipping as well -- so we end up// compositing with an incorrect clip. This only seems to affect// fallback cases, which happen when we have CSS scaling going on.// This will blow away the current path, but we already blew it// away in this function earlier.mThebes->UpdateSurfaceClip();#endifFINISH:if(NS_SUCCEEDED(rv))rv=Redraw(dirty);returnrv;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetGlobalCompositeOperation(constnsAString&op){gfxContext::GraphicsOperatorthebes_op;#define CANVAS_OP_TO_THEBES_OP(cvsop,thebesop) \ if (op.EqualsLiteral(cvsop)) \ thebes_op = gfxContext::OPERATOR_##thebesop;// XXX "darker" isn't really correctCANVAS_OP_TO_THEBES_OP("clear",CLEAR)elseCANVAS_OP_TO_THEBES_OP("copy",SOURCE)elseCANVAS_OP_TO_THEBES_OP("darker",SATURATE)// XXXelseCANVAS_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)// not part of spec, kept here for compatelseCANVAS_OP_TO_THEBES_OP("over",OVER)elsereturnNS_ERROR_NOT_IMPLEMENTED;#undef CANVAS_OP_TO_THEBES_OPmThebes->SetOperator(thebes_op);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetGlobalCompositeOperation(nsAString&op){gfxContext::GraphicsOperatorthebes_op=mThebes->CurrentOperator();#define CANVAS_OP_TO_THEBES_OP(cvsop,thebesop) \ if (thebes_op == gfxContext::OPERATOR_##thebesop) \ op.AssignLiteral(cvsop);// XXX "darker" isn't really correctCANVAS_OP_TO_THEBES_OP("clear",CLEAR)elseCANVAS_OP_TO_THEBES_OP("copy",SOURCE)elseCANVAS_OP_TO_THEBES_OP("darker",SATURATE)// XXXelseCANVAS_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;}staticvoidFlushLayoutForTree(nsIDOMWindow*aWindow){nsCOMPtr<nsPIDOMWindow>piWin=do_QueryInterface(aWindow);if(!piWin)return;// Note that because FlushPendingNotifications flushes parents, this// is O(N^2) in docshell tree depth. However, the docshell tree is// usually pretty shallow.nsCOMPtr<nsIDOMDocument>domDoc;aWindow->GetDocument(getter_AddRefs(domDoc));nsCOMPtr<nsIDocument>doc=do_QueryInterface(domDoc);if(doc){doc->FlushPendingNotifications(Flush_Layout);}nsCOMPtr<nsIDocShellTreeNode>node=do_QueryInterface(piWin->GetDocShell());if(node){PRInt32i=0,i_end;node->GetChildCount(&i_end);for(;i<i_end;++i){nsCOMPtr<nsIDocShellTreeItem>item;node->GetChildAt(i,getter_AddRefs(item));nsCOMPtr<nsIDOMWindow>win=do_GetInterface(item);if(win){FlushLayoutForTree(win);}}}}NS_IMETHODIMPnsCanvasRenderingContext2D::DrawWindow(nsIDOMWindow*aWindow,floataX,floataY,floataW,floataH,constnsAString&aBGColor,PRUint32flags){NS_ENSURE_ARG(aWindow!=nsnull);// protect against too-large surfaces that will cause allocation// or overflow issuesif(!gfxASurface::CheckSurfaceSize(gfxIntSize(aW,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 updatesPRBoolskipFlush=(flags&nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH)!=0;if(!skipFlush)FlushLayoutForTree(aWindow);nsCOMPtr<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;nsresultrv=mCSSParser->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;if(flags&nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_CARET){renderDocFlags|=nsIPresShell::RENDER_CARET;}if(flags&nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_VIEW){renderDocFlags&=~nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;}PRBoololdDisableValue=nsLayoutUtils::sDisableGetUsedXAssertions;nsLayoutUtils::sDisableGetUsedXAssertions=oldDisableValue||skipFlush;presShell->RenderDocument(r,renderDocFlags,bgColor,mThebes);nsLayoutUtils::sDisableGetUsedXAssertions=oldDisableValue;// 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.gfxRectdamageRect=mThebes->UserToDevice(gfxRect(0,0,aW,aH));Redraw(damageRect);returnrv;}//// device pixel getting/setting//extern"C"{#include"jstypes.h"JS_FRIEND_API(JSBool)js_CoerceArrayToCanvasImageData(JSObject*obj,jsuintoffset,jsuintcount,JSUint8*dest);JS_FRIEND_API(JSObject*)js_NewArrayObjectWithCapacity(JSContext*cx,jsuintcapacity,jsval**vector);}// ImageData getImageData (in float x, in float y, in float width, in float height);NS_IMETHODIMPnsCanvasRenderingContext2D::GetImageData(){if(!mValid)returnNS_ERROR_FAILURE;if(mCanvasElement&&mCanvasElement->IsWriteOnly()&&!nsContentUtils::IsCallerTrustedForRead()){// XXX ERRMSG we need to report an error to developers here! (bug 329026)returnNS_ERROR_DOM_SECURITY_ERR;}nsAXPCNativeCallContext*ncc=nsnull;nsresultrv=nsContentUtils::XPConnect()->GetCurrentNativeCallContext(&ncc);NS_ENSURE_SUCCESS(rv,rv);if(!ncc)returnNS_ERROR_FAILURE;JSContext*ctx=nsnull;rv=ncc->GetJSContext(&ctx);NS_ENSURE_SUCCESS(rv,rv);PRUint32argc;jsval*argv=nsnull;ncc->GetArgc(&argc);ncc->GetArgvPtr(&argv);JSAutoRequestar(ctx);int32x,y,w,h;if(!JS_ConvertArguments(ctx,argc,argv,"jjjj",&x,&y,&w,&h))returnNS_ERROR_DOM_SYNTAX_ERR;if(!CanvasUtils::CheckSaneSubrectSize(x,y,w,h,mWidth,mHeight))returnNS_ERROR_DOM_SYNTAX_ERR;nsAutoArrayPtr<PRUint8>surfaceData(new(std::nothrow)PRUint8[w*h*4]);intsurfaceDataStride=w*4;intsurfaceDataOffset=0;if(!surfaceData)returnNS_ERROR_OUT_OF_MEMORY;nsRefPtr<gfxImageSurface>tmpsurf=newgfxImageSurface(surfaceData,gfxIntSize(w,h),w*4,gfxASurface::ImageFormatARGB32);if(!tmpsurf||tmpsurf->CairoStatus())returnNS_ERROR_FAILURE;nsRefPtr<gfxContext>tmpctx=newgfxContext(tmpsurf);if(!tmpctx||tmpctx->HasError())returnNS_ERROR_FAILURE;tmpctx->SetOperator(gfxContext::OPERATOR_SOURCE);tmpctx->SetSource(mSurface,gfxPoint(-(int)x,-(int)y));tmpctx->Paint();tmpctx=nsnull;tmpsurf=nsnull;PRUint32len=w*h*4;if(len>(((PRUint32)0xfff00000)/sizeof(jsval)))returnNS_ERROR_INVALID_ARG;jsval*dest;JSObject*dataArray=js_NewArrayObjectWithCapacity(ctx,len,&dest);if(!dataArray)returnNS_ERROR_OUT_OF_MEMORY;nsAutoGCRootarrayGCRoot(&dataArray,&rv);NS_ENSURE_SUCCESS(rv,rv);PRUint8*row;for(intj=0;j<h;j++){row=surfaceData+surfaceDataOffset+(surfaceDataStride*j);for(inti=0;i<w;i++){// XXX Is there some useful swizzle MMX we can use here?// I guess we have to INT_TO_JSVAL still#ifdef IS_LITTLE_ENDIANPRUint8b=*row++;PRUint8g=*row++;PRUint8r=*row++;PRUint8a=*row++;#elsePRUint8a=*row++;PRUint8r=*row++;PRUint8g=*row++;PRUint8b=*row++;#endif// Convert to non-premultiplied colorif(a!=0){r=(r*255)/a;g=(g*255)/a;b=(b*255)/a;}*dest++=INT_TO_JSVAL(r);*dest++=INT_TO_JSVAL(g);*dest++=INT_TO_JSVAL(b);*dest++=INT_TO_JSVAL(a);}}// Allocate result object after array, so if we have to trigger gc// we do it now.JSObject*result=JS_NewObject(ctx,NULL,NULL,NULL);if(!result)returnNS_ERROR_OUT_OF_MEMORY;nsAutoGCRootresultGCRoot(&result,&rv);NS_ENSURE_SUCCESS(rv,rv);if(!JS_DefineProperty(ctx,result,"width",INT_TO_JSVAL(w),NULL,NULL,0)||!JS_DefineProperty(ctx,result,"height",INT_TO_JSVAL(h),NULL,NULL,0)||!JS_DefineProperty(ctx,result,"data",OBJECT_TO_JSVAL(dataArray),NULL,NULL,0))returnNS_ERROR_FAILURE;jsval*retvalPtr;ncc->GetRetValPtr(&retvalPtr);*retvalPtr=OBJECT_TO_JSVAL(result);ncc->SetReturnValueWasSet(PR_TRUE);returnNS_OK;}staticinlinePRUint8ToUint8(jsintaInput){if(PRUint32(aInput)>255)return(aInput<0)?0:255;returnPRUint8(aInput);}staticinlinePRUint8ToUint8(doubleaInput){if(!(aInput>=0))/* Not < so that NaN coerces to 0 */return0;if(aInput>255)return255;doubletoTruncate=aInput+0.5;PRUint8retval=PRUint8(toTruncate);// now retval is rounded to nearest, ties rounded up. We want// rounded to nearest ties to even, so check whether we had a tie.if(retval==toTruncate){// It was a tie (since adding 0.5 gave us the exact integer we want).// Since we rounded up, we either already have an even number or we// have an odd number but the number we want is one less. So just// unconditionally masking out the ones bit should do the trick to get// us the value we want.return(retval&~1);}returnretval;}// void putImageData (in ImageData d, in float x, in float y);NS_IMETHODIMPnsCanvasRenderingContext2D::PutImageData(){nsresultrv;if(!mValid)returnNS_ERROR_FAILURE;nsAXPCNativeCallContext*ncc=nsnull;rv=nsContentUtils::XPConnect()->GetCurrentNativeCallContext(&ncc);NS_ENSURE_SUCCESS(rv,rv);if(!ncc)returnNS_ERROR_FAILURE;JSContext*ctx=nsnull;rv=ncc->GetJSContext(&ctx);NS_ENSURE_SUCCESS(rv,rv);PRUint32argc;jsval*argv=nsnull;ncc->GetArgc(&argc);ncc->GetArgvPtr(&argv);JSAutoRequestar(ctx);JSObject*dataObject;int32x,y;if(!JS_ConvertArguments(ctx,argc,argv,"ojj",&dataObject,&x,&y))returnNS_ERROR_DOM_SYNTAX_ERR;if(!dataObject)returnNS_ERROR_DOM_SYNTAX_ERR;int32w,h;JSObject*dataArray;jsvalv;if(!JS_GetProperty(ctx,dataObject,"width",&v)||!JS_ValueToInt32(ctx,v,&w))returnNS_ERROR_DOM_SYNTAX_ERR;if(!JS_GetProperty(ctx,dataObject,"height",&v)||!JS_ValueToInt32(ctx,v,&h))returnNS_ERROR_DOM_SYNTAX_ERR;if(!JS_GetProperty(ctx,dataObject,"data",&v)||!JSVAL_IS_OBJECT(v))returnNS_ERROR_DOM_SYNTAX_ERR;dataArray=JSVAL_TO_OBJECT(v);if(!CanvasUtils::CheckSaneSubrectSize(x,y,w,h,mWidth,mHeight))returnNS_ERROR_DOM_SYNTAX_ERR;jsuintarrayLen;if(!JS_IsArrayObject(ctx,dataArray)||!JS_GetArrayLength(ctx,dataArray,&arrayLen)||arrayLen<(jsuint)(w*h*4))returnNS_ERROR_DOM_SYNTAX_ERR;nsAutoArrayPtr<PRUint8>imageBuffer(new(std::nothrow)PRUint8[w*h*4]);if(!imageBuffer)returnNS_ERROR_OUT_OF_MEMORY;PRUint8*imgPtr=imageBuffer.get();JSBoolcanFastPath=js_CoerceArrayToCanvasImageData(dataArray,0,w*h*4,imageBuffer);// no fast path? go slow. We sadly need this for now, instead of just// throwing, because dataArray might not be dense in case someone stuck// their own array on the imageData.// FIXME: it'd be awfully nice if we could prevent such modification of// imageData objects, since it's likely the spec won't allow it anyway.// Bug 497110 covers this.if(!canFastPath){jsvalvr,vg,vb,va;PRUint8ir,ig,ib,ia;for(int32j=0;j<h;j++){int32lineOffset=(j*w*4);for(int32i=0;i<w;i++){int32pixelOffset=lineOffset+i*4;if(!JS_GetElement(ctx,dataArray,pixelOffset+0,&vr)||!JS_GetElement(ctx,dataArray,pixelOffset+1,&vg)||!JS_GetElement(ctx,dataArray,pixelOffset+2,&vb)||!JS_GetElement(ctx,dataArray,pixelOffset+3,&va))returnNS_ERROR_DOM_SYNTAX_ERR;if(JSVAL_IS_INT(vr))ir=ToUint8(JSVAL_TO_INT(vr));elseif(JSVAL_IS_DOUBLE(vr))ir=ToUint8(*JSVAL_TO_DOUBLE(vr));elsereturnNS_ERROR_DOM_SYNTAX_ERR;if(JSVAL_IS_INT(vg))ig=ToUint8(JSVAL_TO_INT(vg));elseif(JSVAL_IS_DOUBLE(vg))ig=ToUint8(*JSVAL_TO_DOUBLE(vg));elsereturnNS_ERROR_DOM_SYNTAX_ERR;if(JSVAL_IS_INT(vb))ib=ToUint8(JSVAL_TO_INT(vb));elseif(JSVAL_IS_DOUBLE(vb))ib=ToUint8(*JSVAL_TO_DOUBLE(vb));elsereturnNS_ERROR_DOM_SYNTAX_ERR;if(JSVAL_IS_INT(va))ia=ToUint8(JSVAL_TO_INT(va));elseif(JSVAL_IS_DOUBLE(va))ia=ToUint8(*JSVAL_TO_DOUBLE(va));elsereturnNS_ERROR_DOM_SYNTAX_ERR;// Convert to premultiplied color (losslessly if the input came from getImageData)ir=(ir*ia+254)/255;ig=(ig*ia+254)/255;ib=(ib*ia+254)/255;#ifdef IS_LITTLE_ENDIAN*imgPtr++=ib;*imgPtr++=ig;*imgPtr++=ir;*imgPtr++=ia;#else*imgPtr++=ia;*imgPtr++=ir;*imgPtr++=ig;*imgPtr++=ib;#endif}}}else{/* Walk through and premultiply and swap rgba *//* XXX SSE me */PRUint8ir,ig,ib,ia;PRUint8*ptr=imgPtr;for(int32i=0;i<w*h;i++){ir=ptr[0];ig=ptr[1];ib=ptr[2];ia=ptr[3];#ifdef IS_LITTLE_ENDIANptr[0]=(ib*ia+254)/255;ptr[1]=(ig*ia+254)/255;ptr[2]=(ir*ia+254)/255;#elseptr[0]=ia;ptr[1]=(ir*ia+254)/255;ptr[2]=(ig*ia+254)/255;ptr[3]=(ib*ia+254)/255;#endifptr+=4;}}nsRefPtr<gfxImageSurface>imgsurf=newgfxImageSurface(imageBuffer.get(),gfxIntSize(w,h),w*4,gfxASurface::ImageFormatARGB32);if(!imgsurf||imgsurf->CairoStatus())returnNS_ERROR_FAILURE;gfxContextPathAutoSaveRestorepathSR(mThebes);gfxContextAutoSaveRestoreautoSR(mThebes);// ignore clipping region, as per specmThebes->ResetClip();mThebes->IdentityMatrix();mThebes->Translate(gfxPoint(x,y));mThebes->NewPath();mThebes->Rectangle(gfxRect(0,0,w,h));mThebes->SetSource(imgsurf,gfxPoint(0,0));mThebes->SetOperator(gfxContext::OPERATOR_SOURCE);mThebes->Fill();returnRedraw();}NS_IMETHODIMPnsCanvasRenderingContext2D::GetThebesSurface(gfxASurface**surface){if(!mSurface){*surface=nsnull;returnNS_ERROR_NOT_AVAILABLE;}*surface=mSurface.get();NS_ADDREF(*surface);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::CreateImageData(){if(!mValid)returnNS_ERROR_FAILURE;nsAXPCNativeCallContext*ncc=nsnull;nsresultrv=nsContentUtils::XPConnect()->GetCurrentNativeCallContext(&ncc);NS_ENSURE_SUCCESS(rv,rv);if(!ncc)returnNS_ERROR_FAILURE;JSContext*ctx=nsnull;rv=ncc->GetJSContext(&ctx);NS_ENSURE_SUCCESS(rv,rv);PRUint32argc;jsval*argv=nsnull;ncc->GetArgc(&argc);ncc->GetArgvPtr(&argv);JSAutoRequestar(ctx);int32width,height;if(!JS_ConvertArguments(ctx,argc,argv,"jj",&width,&height))returnNS_ERROR_DOM_SYNTAX_ERR;if(width<=0||height<=0)returnNS_ERROR_DOM_INDEX_SIZE_ERR;PRUint32w=(PRUint32)width;PRUint32h=(PRUint32)height;// check for overflow when calculating lenPRUint32len0=w*h;if(len0/w!=(PRUint32)h)returnNS_ERROR_DOM_INDEX_SIZE_ERR;PRUint32len=len0*4;if(len/4!=len0)returnNS_ERROR_DOM_INDEX_SIZE_ERR;jsval*dest;JSObject*dataArray=js_NewArrayObjectWithCapacity(ctx,len,&dest);if(!dataArray)returnNS_ERROR_OUT_OF_MEMORY;nsAutoGCRootarrayGCRoot(&dataArray,&rv);NS_ENSURE_SUCCESS(rv,rv);for(PRUint32i=0;i<len;i++)*dest++=JSVAL_ZERO;// Allocate result object after array, so if we have to trigger gc// we do it now.JSObject*result=JS_NewObject(ctx,NULL,NULL,NULL);if(!result)returnNS_ERROR_OUT_OF_MEMORY;nsAutoGCRootresultGCRoot(&result,&rv);NS_ENSURE_SUCCESS(rv,rv);if(!JS_DefineProperty(ctx,result,"width",INT_TO_JSVAL(w),NULL,NULL,0)||!JS_DefineProperty(ctx,result,"height",INT_TO_JSVAL(h),NULL,NULL,0)||!JS_DefineProperty(ctx,result,"data",OBJECT_TO_JSVAL(dataArray),NULL,NULL,0))returnNS_ERROR_FAILURE;jsval*retvalPtr;ncc->GetRetValPtr(&retvalPtr);*retvalPtr=OBJECT_TO_JSVAL(result);ncc->SetReturnValueWasSet(PR_TRUE);returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::GetMozImageSmoothingEnabled(PRBool*retVal){*retVal=CurrentState().imageSmoothingEnabled;returnNS_OK;}NS_IMETHODIMPnsCanvasRenderingContext2D::SetMozImageSmoothingEnabled(PRBoolval){if(val!=CurrentState().imageSmoothingEnabled){CurrentState().imageSmoothingEnabled=val;DirtyAllStyles();}returnNS_OK;}