1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Corporation code.
16 : *
17 : * The Initial Developer of the Original Code is Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2011
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Bas Schouten <bschouten@mozilla.com>
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "base/basictypes.h"
39 :
40 : #include "nsIDOMXULElement.h"
41 :
42 : #include "prmem.h"
43 : #include "prenv.h"
44 :
45 : #include "nsIServiceManager.h"
46 : #include "nsMathUtils.h"
47 :
48 : #include "nsContentUtils.h"
49 :
50 : #include "nsIDOMDocument.h"
51 : #include "nsIDocument.h"
52 : #include "nsIDOMCanvasRenderingContext2D.h"
53 : #include "nsICanvasRenderingContextInternal.h"
54 : #include "nsHTMLCanvasElement.h"
55 : #include "nsSVGEffects.h"
56 : #include "nsPresContext.h"
57 : #include "nsIPresShell.h"
58 : #include "nsIVariant.h"
59 :
60 : #include "nsIInterfaceRequestorUtils.h"
61 : #include "nsIFrame.h"
62 : #include "nsDOMError.h"
63 : #include "nsIScriptError.h"
64 :
65 : #include "nsCSSParser.h"
66 : #include "mozilla/css/StyleRule.h"
67 : #include "mozilla/css/Declaration.h"
68 : #include "nsComputedDOMStyle.h"
69 : #include "nsStyleSet.h"
70 :
71 : #include "nsPrintfCString.h"
72 :
73 : #include "nsReadableUtils.h"
74 :
75 : #include "nsColor.h"
76 : #include "nsGfxCIID.h"
77 : #include "nsIScriptSecurityManager.h"
78 : #include "nsIDocShell.h"
79 : #include "nsIDOMWindow.h"
80 : #include "nsPIDOMWindow.h"
81 : #include "nsIDocShell.h"
82 : #include "nsIDocShellTreeItem.h"
83 : #include "nsIDocShellTreeNode.h"
84 : #include "nsIXPConnect.h"
85 : #include "jsapi.h"
86 : #include "nsDisplayList.h"
87 :
88 : #include "nsTArray.h"
89 :
90 : #include "imgIEncoder.h"
91 :
92 : #include "gfxContext.h"
93 : #include "gfxASurface.h"
94 : #include "gfxImageSurface.h"
95 : #include "gfxPlatform.h"
96 : #include "gfxFont.h"
97 : #include "gfxBlur.h"
98 : #include "gfxUtils.h"
99 :
100 : #include "nsFrameManager.h"
101 : #include "nsFrameLoader.h"
102 : #include "nsBidi.h"
103 : #include "nsBidiPresUtils.h"
104 : #include "Layers.h"
105 : #include "CanvasUtils.h"
106 : #include "nsIMemoryReporter.h"
107 : #include "nsStyleUtil.h"
108 : #include "CanvasImageCache.h"
109 :
110 : #include <algorithm>
111 : #include "mozilla/dom/ContentParent.h"
112 : #include "mozilla/ipc/PDocumentRendererParent.h"
113 : #include "mozilla/dom/PBrowserParent.h"
114 : #include "mozilla/ipc/DocumentRendererParent.h"
115 :
116 : #include "mozilla/gfx/2D.h"
117 : #include "mozilla/gfx/PathHelpers.h"
118 : #include "mozilla/Preferences.h"
119 :
120 : #ifdef XP_WIN
121 : #include "gfxWindowsPlatform.h"
122 : #endif
123 :
124 : // windows.h (included by chromium code) defines this, in its infinite wisdom
125 : #undef DrawText
126 :
127 : using namespace mozilla;
128 : using namespace mozilla::CanvasUtils;
129 : using namespace mozilla::css;
130 : using namespace mozilla::dom;
131 : using namespace mozilla::gfx;
132 : using namespace mozilla::ipc;
133 : using namespace mozilla::layers;
134 :
135 : namespace mgfx = mozilla::gfx;
136 :
137 : static float kDefaultFontSize = 10.0;
138 1464 : static NS_NAMED_LITERAL_STRING(kDefaultFontName, "sans-serif");
139 1464 : static NS_NAMED_LITERAL_STRING(kDefaultFontStyle, "10px sans-serif");
140 :
141 : /* Memory reporter stuff */
142 : static nsIMemoryReporter *gCanvasAzureMemoryReporter = nsnull;
143 : static PRInt64 gCanvasAzureMemoryUsed = 0;
144 :
145 0 : static PRInt64 GetCanvasAzureMemoryUsed() {
146 0 : return gCanvasAzureMemoryUsed;
147 : }
148 :
149 : // This is KIND_OTHER because it's not always clear where in memory the pixels
150 : // of a canvas are stored. Furthermore, this memory will be tracked by the
151 : // underlying surface implementations. See bug 655638 for details.
152 0 : NS_MEMORY_REPORTER_IMPLEMENT(CanvasAzureMemory,
153 : "canvas-2d-pixel-bytes",
154 : KIND_OTHER,
155 : UNITS_BYTES,
156 : GetCanvasAzureMemoryUsed,
157 : "Memory used by 2D canvases. Each canvas requires (width * height * 4) "
158 0 : "bytes.")
159 :
160 : /**
161 : ** nsCanvasGradientAzure
162 : **/
163 : #define NS_CANVASGRADIENTAZURE_PRIVATE_IID \
164 : {0x28425a6a, 0x90e0, 0x4d42, {0x9c, 0x75, 0xff, 0x60, 0x09, 0xb3, 0x10, 0xa8}}
165 : class nsCanvasGradientAzure : public nsIDOMCanvasGradient
166 : {
167 : public:
168 : NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASGRADIENTAZURE_PRIVATE_IID)
169 :
170 : enum Type
171 : {
172 : LINEAR = 0,
173 : RADIAL
174 : };
175 :
176 0 : Type GetType()
177 : {
178 0 : return mType;
179 : }
180 :
181 :
182 0 : GradientStops *GetGradientStopsForTarget(DrawTarget *aRT)
183 : {
184 0 : if (mStops && mStops->GetBackendType() == aRT->GetType()) {
185 0 : return mStops;
186 : }
187 :
188 0 : mStops = aRT->CreateGradientStops(mRawStops.Elements(), mRawStops.Length());
189 :
190 0 : return mStops;
191 : }
192 :
193 : NS_DECL_ISUPPORTS
194 :
195 : /* nsIDOMCanvasGradient */
196 0 : NS_IMETHOD AddColorStop (float offset,
197 : const nsAString& colorstr)
198 : {
199 0 : if (!FloatValidate(offset) || offset < 0.0 || offset > 1.0) {
200 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
201 : }
202 :
203 : nscolor color;
204 0 : nsCSSParser parser;
205 0 : nsresult rv = parser.ParseColorString(nsString(colorstr),
206 0 : nsnull, 0, &color);
207 0 : if (NS_FAILED(rv)) {
208 0 : return NS_ERROR_DOM_SYNTAX_ERR;
209 : }
210 :
211 0 : mStops = nsnull;
212 :
213 0 : GradientStop newStop;
214 :
215 0 : newStop.offset = offset;
216 0 : newStop.color = Color::FromABGR(color);
217 :
218 0 : mRawStops.AppendElement(newStop);
219 :
220 0 : return NS_OK;
221 : }
222 :
223 : protected:
224 0 : nsCanvasGradientAzure(Type aType) : mType(aType)
225 0 : {}
226 :
227 : nsTArray<GradientStop> mRawStops;
228 : RefPtr<GradientStops> mStops;
229 : Type mType;
230 0 : virtual ~nsCanvasGradientAzure() {}
231 : };
232 :
233 : class nsCanvasRadialGradientAzure : public nsCanvasGradientAzure
234 0 : {
235 : public:
236 0 : nsCanvasRadialGradientAzure(const Point &aBeginOrigin, Float aBeginRadius,
237 : const Point &aEndOrigin, Float aEndRadius)
238 : : nsCanvasGradientAzure(RADIAL)
239 : , mCenter1(aBeginOrigin)
240 : , mCenter2(aEndOrigin)
241 : , mRadius1(aBeginRadius)
242 0 : , mRadius2(aEndRadius)
243 : {
244 0 : }
245 :
246 : Point mCenter1;
247 : Point mCenter2;
248 : Float mRadius1;
249 : Float mRadius2;
250 : };
251 :
252 : class nsCanvasLinearGradientAzure : public nsCanvasGradientAzure
253 0 : {
254 : public:
255 0 : nsCanvasLinearGradientAzure(const Point &aBegin, const Point &aEnd)
256 : : nsCanvasGradientAzure(LINEAR)
257 : , mBegin(aBegin)
258 0 : , mEnd(aEnd)
259 : {
260 0 : }
261 :
262 : protected:
263 : friend class nsCanvasRenderingContext2DAzure;
264 :
265 : // Beginning of linear gradient.
266 : Point mBegin;
267 : // End of linear gradient.
268 : Point mEnd;
269 : };
270 :
271 : NS_DEFINE_STATIC_IID_ACCESSOR(nsCanvasGradientAzure, NS_CANVASGRADIENTAZURE_PRIVATE_IID)
272 :
273 0 : NS_IMPL_ADDREF(nsCanvasGradientAzure)
274 0 : NS_IMPL_RELEASE(nsCanvasGradientAzure)
275 :
276 : // XXX
277 : // DOMCI_DATA(CanvasGradient, nsCanvasGradientAzure)
278 :
279 0 : NS_INTERFACE_MAP_BEGIN(nsCanvasGradientAzure)
280 0 : NS_INTERFACE_MAP_ENTRY(nsCanvasGradientAzure)
281 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasGradient)
282 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CanvasGradient)
283 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
284 0 : NS_INTERFACE_MAP_END
285 :
286 : /**
287 : ** nsCanvasPatternAzure
288 : **/
289 : #define NS_CANVASPATTERNAZURE_PRIVATE_IID \
290 : {0xc9bacc25, 0x28da, 0x421e, {0x9a, 0x4b, 0xbb, 0xd6, 0x93, 0x05, 0x12, 0xbc}}
291 : class nsCanvasPatternAzure MOZ_FINAL : public nsIDOMCanvasPattern
292 0 : {
293 : public:
294 : NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASPATTERNAZURE_PRIVATE_IID)
295 :
296 : enum RepeatMode
297 : {
298 : REPEAT,
299 : REPEATX,
300 : REPEATY,
301 : NOREPEAT
302 : };
303 :
304 0 : nsCanvasPatternAzure(SourceSurface* aSurface,
305 : RepeatMode aRepeat,
306 : nsIPrincipal* principalForSecurityCheck,
307 : bool forceWriteOnly,
308 : bool CORSUsed)
309 : : mSurface(aSurface)
310 : , mRepeat(aRepeat)
311 : , mPrincipal(principalForSecurityCheck)
312 : , mForceWriteOnly(forceWriteOnly)
313 0 : , mCORSUsed(CORSUsed)
314 : {
315 0 : }
316 :
317 : NS_DECL_ISUPPORTS
318 :
319 : RefPtr<SourceSurface> mSurface;
320 : const RepeatMode mRepeat;
321 : nsCOMPtr<nsIPrincipal> mPrincipal;
322 : const bool mForceWriteOnly;
323 : const bool mCORSUsed;
324 : };
325 :
326 : NS_DEFINE_STATIC_IID_ACCESSOR(nsCanvasPatternAzure, NS_CANVASPATTERNAZURE_PRIVATE_IID)
327 :
328 0 : NS_IMPL_ADDREF(nsCanvasPatternAzure)
329 0 : NS_IMPL_RELEASE(nsCanvasPatternAzure)
330 :
331 : // XXX
332 : // DOMCI_DATA(CanvasPattern, nsCanvasPatternAzure)
333 :
334 0 : NS_INTERFACE_MAP_BEGIN(nsCanvasPatternAzure)
335 0 : NS_INTERFACE_MAP_ENTRY(nsCanvasPatternAzure)
336 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasPattern)
337 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CanvasPattern)
338 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
339 0 : NS_INTERFACE_MAP_END
340 :
341 : /**
342 : ** nsTextMetricsAzure
343 : **/
344 : #define NS_TEXTMETRICSAZURE_PRIVATE_IID \
345 : {0x9793f9e7, 0x9dc1, 0x4e9c, {0x81, 0xc8, 0xfc, 0xa7, 0x14, 0xf4, 0x30, 0x79}}
346 : class nsTextMetricsAzure : public nsIDOMTextMetrics
347 : {
348 : public:
349 0 : nsTextMetricsAzure(float w) : width(w) { }
350 :
351 0 : virtual ~nsTextMetricsAzure() { }
352 :
353 : NS_DECLARE_STATIC_IID_ACCESSOR(NS_TEXTMETRICSAZURE_PRIVATE_IID)
354 :
355 0 : NS_IMETHOD GetWidth(float* w) {
356 0 : *w = width;
357 0 : return NS_OK;
358 : }
359 :
360 : NS_DECL_ISUPPORTS
361 :
362 : private:
363 : float width;
364 : };
365 :
366 : NS_DEFINE_STATIC_IID_ACCESSOR(nsTextMetricsAzure, NS_TEXTMETRICSAZURE_PRIVATE_IID)
367 :
368 0 : NS_IMPL_ADDREF(nsTextMetricsAzure)
369 0 : NS_IMPL_RELEASE(nsTextMetricsAzure)
370 :
371 : // XXX
372 : // DOMCI_DATA(TextMetrics, nsTextMetricsAzure)
373 :
374 0 : NS_INTERFACE_MAP_BEGIN(nsTextMetricsAzure)
375 0 : NS_INTERFACE_MAP_ENTRY(nsTextMetricsAzure)
376 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMTextMetrics)
377 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TextMetrics)
378 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
379 0 : NS_INTERFACE_MAP_END
380 :
381 : struct nsCanvasBidiProcessorAzure;
382 :
383 : // Cap sigma to avoid overly large temp surfaces.
384 : static const Float SIGMA_MAX = 100;
385 :
386 : /**
387 : ** nsCanvasRenderingContext2DAzure
388 : **/
389 : class nsCanvasRenderingContext2DAzure :
390 : public nsIDOMCanvasRenderingContext2D,
391 : public nsICanvasRenderingContextInternal
392 : {
393 : public:
394 : nsCanvasRenderingContext2DAzure();
395 : virtual ~nsCanvasRenderingContext2DAzure();
396 :
397 : nsresult Redraw();
398 :
399 : // nsICanvasRenderingContextInternal
400 : NS_IMETHOD SetCanvasElement(nsHTMLCanvasElement* aParentCanvas);
401 : NS_IMETHOD SetDimensions(PRInt32 width, PRInt32 height);
402 0 : NS_IMETHOD InitializeWithSurface(nsIDocShell *shell, gfxASurface *surface, PRInt32 width, PRInt32 height)
403 0 : { return NS_ERROR_NOT_IMPLEMENTED; }
404 :
405 : NS_IMETHOD Render(gfxContext *ctx, gfxPattern::GraphicsFilter aFilter);
406 : NS_IMETHOD GetInputStream(const char* aMimeType,
407 : const PRUnichar* aEncoderOptions,
408 : nsIInputStream **aStream);
409 : NS_IMETHOD GetThebesSurface(gfxASurface **surface);
410 :
411 0 : TemporaryRef<SourceSurface> GetSurfaceSnapshot()
412 0 : { return mTarget ? mTarget->Snapshot() : nsnull; }
413 :
414 : NS_IMETHOD SetIsOpaque(bool isOpaque);
415 : NS_IMETHOD Reset();
416 : already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
417 : CanvasLayer *aOldLayer,
418 : LayerManager *aManager);
419 : void MarkContextClean();
420 : NS_IMETHOD SetIsIPC(bool isIPC);
421 : // this rect is in canvas device space
422 : void Redraw(const mgfx::Rect &r);
423 0 : NS_IMETHOD Redraw(const gfxRect &r) { Redraw(ToRect(r)); return NS_OK; }
424 :
425 : // this rect is in mTarget's current user space
426 : nsresult RedrawUser(const gfxRect &r);
427 :
428 : // nsISupports interface + CC
429 0 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
430 :
431 1464 : NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsCanvasRenderingContext2DAzure, nsIDOMCanvasRenderingContext2D)
432 :
433 : // nsIDOMCanvasRenderingContext2D interface
434 : NS_DECL_NSIDOMCANVASRENDERINGCONTEXT2D
435 :
436 : enum Style {
437 : STYLE_STROKE = 0,
438 : STYLE_FILL,
439 : STYLE_MAX
440 : };
441 :
442 : nsresult LineTo(const Point& aPoint);
443 : nsresult BezierTo(const Point& aCP1, const Point& aCP2, const Point& aCP3);
444 :
445 : protected:
446 : nsresult InitializeWithTarget(DrawTarget *surface, PRInt32 width, PRInt32 height);
447 :
448 : /**
449 : * The number of living nsCanvasRenderingContexts. When this goes down to
450 : * 0, we free the premultiply and unpremultiply tables, if they exist.
451 : */
452 : static PRUint32 sNumLivingContexts;
453 :
454 : /**
455 : * Lookup table used to speed up GetImageData().
456 : */
457 : static PRUint8 (*sUnpremultiplyTable)[256];
458 :
459 : /**
460 : * Lookup table used to speed up PutImageData().
461 : */
462 : static PRUint8 (*sPremultiplyTable)[256];
463 :
464 : // Some helpers. Doesn't modify a color on failure.
465 : nsresult SetStyleFromStringOrInterface(const nsAString& aStr, nsISupports *aInterface, Style aWhichStyle);
466 : nsresult GetStyleAsStringOrInterface(nsAString& aStr, nsISupports **aInterface, PRInt32 *aType, Style aWhichStyle);
467 :
468 : void StyleColorToString(const nscolor& aColor, nsAString& aStr);
469 :
470 : /**
471 : * Creates the unpremultiply lookup table, if it doesn't exist.
472 : */
473 : void EnsureUnpremultiplyTable();
474 :
475 : /**
476 : * Creates the premultiply lookup table, if it doesn't exist.
477 : */
478 : void EnsurePremultiplyTable();
479 :
480 : /* This function ensures there is a writable pathbuilder available, this
481 : * pathbuilder may be working in user space or in device space or
482 : * device space.
483 : */
484 : void EnsureWritablePath();
485 :
486 : // Ensures a path in UserSpace is available.
487 : void EnsureUserSpacePath();
488 :
489 : void TransformWillUpdate();
490 :
491 : // Report the fillRule has changed.
492 : void FillRuleChanged();
493 :
494 : /**
495 : * Returns the surface format this canvas should be allocated using. Takes
496 : * into account mOpaque, platform requirements, etc.
497 : */
498 : SurfaceFormat GetSurfaceFormat() const;
499 :
500 0 : nsHTMLCanvasElement *HTMLCanvasElement() {
501 0 : return static_cast<nsHTMLCanvasElement*>(mCanvasElement.get());
502 : }
503 :
504 : // Member vars
505 : PRInt32 mWidth, mHeight;
506 :
507 : // This is true when the canvas is valid, false otherwise, this occurs when
508 : // for some reason initialization of the drawtarget fails. If the canvas
509 : // is invalid certain behavior is expected.
510 : bool mValid;
511 : // This is true when the canvas is valid, but of zero size, this requires
512 : // specific behavior on some operations.
513 : bool mZero;
514 :
515 : bool mOpaque;
516 :
517 : // This is true when the next time our layer is retrieved we need to
518 : // recreate it (i.e. our backing surface changed)
519 : bool mResetLayer;
520 : // This is needed for drawing in drawAsyncXULElement
521 : bool mIPC;
522 :
523 : // the canvas element we're a context of
524 : nsCOMPtr<nsIDOMHTMLCanvasElement> mCanvasElement;
525 :
526 : // If mCanvasElement is not provided, then a docshell is
527 : nsCOMPtr<nsIDocShell> mDocShell;
528 :
529 : // our drawing surfaces, contexts, and layers
530 : RefPtr<DrawTarget> mTarget;
531 :
532 : /**
533 : * Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever
534 : * Redraw is called, reset to false when Render is called.
535 : */
536 : bool mIsEntireFrameInvalid;
537 : /**
538 : * When this is set, the first call to Redraw(gfxRect) should set
539 : * mIsEntireFrameInvalid since we expect it will be followed by
540 : * many more Redraw calls.
541 : */
542 : bool mPredictManyRedrawCalls;
543 :
544 : // This is stored after GetThebesSurface has been called once to avoid
545 : // excessive ThebesSurface initialization overhead.
546 : nsRefPtr<gfxASurface> mThebesSurface;
547 :
548 : /**
549 : * We also have a device space pathbuilder. The reason for this is as
550 : * follows, when a path is being built, but the transform changes, we
551 : * can no longer keep a single path in userspace, considering there's
552 : * several 'user spaces' now. We therefore transform the current path
553 : * into device space, and add all operations to this path in device
554 : * space.
555 : *
556 : * When then finally executing a render, the Azure drawing API expects
557 : * the path to be in userspace. We could then set an identity transform
558 : * on the DrawTarget and do all drawing in device space. This is
559 : * undesirable because it requires transforming patterns, gradients,
560 : * clips, etc. into device space and it would not work for stroking.
561 : * What we do instead is convert the path back to user space when it is
562 : * drawn, and draw it with the current transform. This makes all drawing
563 : * occur correctly.
564 : *
565 : * There's never both a device space path builder and a user space path
566 : * builder present at the same time. There is also never a path and a
567 : * path builder present at the same time. When writing proceeds on an
568 : * existing path the Path is cleared and a new builder is created.
569 : *
570 : * mPath is always in user-space.
571 : */
572 : RefPtr<Path> mPath;
573 : RefPtr<PathBuilder> mDSPathBuilder;
574 : RefPtr<PathBuilder> mPathBuilder;
575 : bool mPathTransformWillUpdate;
576 : Matrix mPathToDS;
577 :
578 : /**
579 : * Number of times we've invalidated before calling redraw
580 : */
581 : PRUint32 mInvalidateCount;
582 : static const PRUint32 kCanvasMaxInvalidateCount = 100;
583 :
584 : /**
585 : * Returns true if a shadow should be drawn along with a
586 : * drawing operation.
587 : */
588 0 : bool NeedToDrawShadow()
589 : {
590 0 : const ContextState& state = CurrentState();
591 :
592 : // The spec says we should not draw shadows if the operator is OVER.
593 : // If it's over and the alpha value is zero, nothing needs to be drawn.
594 : return NS_GET_A(state.shadowColor) != 0 &&
595 0 : (state.shadowBlur != 0 || state.shadowOffset.x != 0 || state.shadowOffset.y != 0);
596 : }
597 :
598 0 : CompositionOp UsedOperation()
599 : {
600 0 : if (NeedToDrawShadow()) {
601 : // In this case the shadow rendering will use the operator.
602 0 : return OP_OVER;
603 : }
604 :
605 0 : return CurrentState().op;
606 : }
607 :
608 : /**
609 : * Gets the pres shell from either the canvas element or the doc shell
610 : */
611 0 : nsIPresShell *GetPresShell() {
612 0 : nsCOMPtr<nsIContent> content = do_QueryObject(mCanvasElement);
613 0 : if (content) {
614 0 : return content->OwnerDoc()->GetShell();
615 : }
616 0 : if (mDocShell) {
617 0 : nsCOMPtr<nsIPresShell> shell;
618 0 : mDocShell->GetPresShell(getter_AddRefs(shell));
619 0 : return shell.get();
620 : }
621 0 : return nsnull;
622 : }
623 :
624 : // text
625 : enum TextAlign {
626 : TEXT_ALIGN_START,
627 : TEXT_ALIGN_END,
628 : TEXT_ALIGN_LEFT,
629 : TEXT_ALIGN_RIGHT,
630 : TEXT_ALIGN_CENTER
631 : };
632 :
633 : enum TextBaseline {
634 : TEXT_BASELINE_TOP,
635 : TEXT_BASELINE_HANGING,
636 : TEXT_BASELINE_MIDDLE,
637 : TEXT_BASELINE_ALPHABETIC,
638 : TEXT_BASELINE_IDEOGRAPHIC,
639 : TEXT_BASELINE_BOTTOM
640 : };
641 :
642 : gfxFontGroup *GetCurrentFontStyle();
643 :
644 : enum TextDrawOperation {
645 : TEXT_DRAW_OPERATION_FILL,
646 : TEXT_DRAW_OPERATION_STROKE,
647 : TEXT_DRAW_OPERATION_MEASURE
648 : };
649 :
650 : /*
651 : * Implementation of the fillText, strokeText, and measure functions with
652 : * the operation abstracted to a flag.
653 : */
654 : nsresult DrawOrMeasureText(const nsAString& text,
655 : float x,
656 : float y,
657 : float maxWidth,
658 : TextDrawOperation op,
659 : float* aWidth);
660 :
661 : // state stack handling
662 0 : class ContextState {
663 : public:
664 0 : ContextState() : textAlign(TEXT_ALIGN_START),
665 : textBaseline(TEXT_BASELINE_ALPHABETIC),
666 : lineWidth(1.0f),
667 : miterLimit(10.0f),
668 : globalAlpha(1.0f),
669 : shadowBlur(0.0),
670 : dashOffset(0.0f),
671 : op(OP_OVER),
672 : fillRule(FILL_WINDING),
673 : lineCap(CAP_BUTT),
674 : lineJoin(JOIN_MITER_OR_BEVEL),
675 0 : imageSmoothingEnabled(true)
676 0 : { }
677 :
678 0 : ContextState(const ContextState& other)
679 : : fontGroup(other.fontGroup),
680 : font(other.font),
681 : textAlign(other.textAlign),
682 : textBaseline(other.textBaseline),
683 : shadowColor(other.shadowColor),
684 : transform(other.transform),
685 : shadowOffset(other.shadowOffset),
686 : lineWidth(other.lineWidth),
687 : miterLimit(other.miterLimit),
688 : globalAlpha(other.globalAlpha),
689 : shadowBlur(other.shadowBlur),
690 : dash(other.dash),
691 : dashOffset(other.dashOffset),
692 : op(other.op),
693 : fillRule(FILL_WINDING),
694 : lineCap(other.lineCap),
695 : lineJoin(other.lineJoin),
696 0 : imageSmoothingEnabled(other.imageSmoothingEnabled)
697 : {
698 0 : for (int i = 0; i < STYLE_MAX; i++) {
699 0 : colorStyles[i] = other.colorStyles[i];
700 0 : gradientStyles[i] = other.gradientStyles[i];
701 0 : patternStyles[i] = other.patternStyles[i];
702 : }
703 0 : }
704 :
705 0 : void SetColorStyle(Style whichStyle, nscolor color) {
706 0 : colorStyles[whichStyle] = color;
707 0 : gradientStyles[whichStyle] = nsnull;
708 0 : patternStyles[whichStyle] = nsnull;
709 0 : }
710 :
711 0 : void SetPatternStyle(Style whichStyle, nsCanvasPatternAzure* pat) {
712 0 : gradientStyles[whichStyle] = nsnull;
713 0 : patternStyles[whichStyle] = pat;
714 0 : }
715 :
716 0 : void SetGradientStyle(Style whichStyle, nsCanvasGradientAzure* grad) {
717 0 : gradientStyles[whichStyle] = grad;
718 0 : patternStyles[whichStyle] = nsnull;
719 0 : }
720 :
721 : /**
722 : * returns true iff the given style is a solid color.
723 : */
724 0 : bool StyleIsColor(Style whichStyle) const
725 : {
726 0 : return !(patternStyles[whichStyle] ||
727 0 : gradientStyles[whichStyle]);
728 : }
729 :
730 :
731 : std::vector<RefPtr<Path> > clipsPushed;
732 :
733 : nsRefPtr<gfxFontGroup> fontGroup;
734 : nsRefPtr<nsCanvasGradientAzure> gradientStyles[STYLE_MAX];
735 : nsRefPtr<nsCanvasPatternAzure> patternStyles[STYLE_MAX];
736 :
737 : nsString font;
738 : TextAlign textAlign;
739 : TextBaseline textBaseline;
740 :
741 : nscolor colorStyles[STYLE_MAX];
742 : nscolor shadowColor;
743 :
744 : Matrix transform;
745 : Point shadowOffset;
746 : Float lineWidth;
747 : Float miterLimit;
748 : Float globalAlpha;
749 : Float shadowBlur;
750 : FallibleTArray<Float> dash;
751 : Float dashOffset;
752 :
753 : CompositionOp op;
754 : FillRule fillRule;
755 : CapStyle lineCap;
756 : JoinStyle lineJoin;
757 :
758 : bool imageSmoothingEnabled;
759 : };
760 :
761 : class GeneralPattern
762 : {
763 : public:
764 0 : GeneralPattern() : mPattern(nsnull) {}
765 0 : ~GeneralPattern()
766 : {
767 0 : if (mPattern) {
768 0 : mPattern->~Pattern();
769 : }
770 0 : }
771 :
772 0 : Pattern& ForStyle(nsCanvasRenderingContext2DAzure *aCtx,
773 : Style aStyle,
774 : DrawTarget *aRT)
775 : {
776 : // This should only be called once or the mPattern destructor will
777 : // not be executed.
778 0 : NS_ASSERTION(!mPattern, "ForStyle() should only be called once on GeneralPattern!");
779 :
780 0 : const nsCanvasRenderingContext2DAzure::ContextState &state = aCtx->CurrentState();
781 :
782 0 : if (state.StyleIsColor(aStyle)) {
783 0 : mPattern = new (mColorPattern.addr()) ColorPattern(Color::FromABGR(state.colorStyles[aStyle]));
784 0 : } else if (state.gradientStyles[aStyle] &&
785 0 : state.gradientStyles[aStyle]->GetType() == nsCanvasGradientAzure::LINEAR) {
786 : nsCanvasLinearGradientAzure *gradient =
787 0 : static_cast<nsCanvasLinearGradientAzure*>(state.gradientStyles[aStyle].get());
788 :
789 0 : mPattern = new (mLinearGradientPattern.addr())
790 : LinearGradientPattern(gradient->mBegin, gradient->mEnd,
791 0 : gradient->GetGradientStopsForTarget(aRT));
792 0 : } else if (state.gradientStyles[aStyle] &&
793 0 : state.gradientStyles[aStyle]->GetType() == nsCanvasGradientAzure::RADIAL) {
794 : nsCanvasRadialGradientAzure *gradient =
795 0 : static_cast<nsCanvasRadialGradientAzure*>(state.gradientStyles[aStyle].get());
796 :
797 0 : mPattern = new (mRadialGradientPattern.addr())
798 : RadialGradientPattern(gradient->mCenter1, gradient->mCenter2, gradient->mRadius1,
799 0 : gradient->mRadius2, gradient->GetGradientStopsForTarget(aRT));
800 0 : } else if (state.patternStyles[aStyle]) {
801 0 : if (aCtx->mCanvasElement) {
802 : CanvasUtils::DoDrawImageSecurityCheck(aCtx->HTMLCanvasElement(),
803 0 : state.patternStyles[aStyle]->mPrincipal,
804 0 : state.patternStyles[aStyle]->mForceWriteOnly,
805 0 : state.patternStyles[aStyle]->mCORSUsed);
806 : }
807 :
808 : ExtendMode mode;
809 0 : if (state.patternStyles[aStyle]->mRepeat == nsCanvasPatternAzure::NOREPEAT) {
810 0 : mode = EXTEND_CLAMP;
811 : } else {
812 0 : mode = EXTEND_REPEAT;
813 : }
814 0 : mPattern = new (mSurfacePattern.addr())
815 0 : SurfacePattern(state.patternStyles[aStyle]->mSurface, mode);
816 : }
817 :
818 0 : return *mPattern;
819 : }
820 :
821 : union {
822 : AlignedStorage2<ColorPattern> mColorPattern;
823 : AlignedStorage2<LinearGradientPattern> mLinearGradientPattern;
824 : AlignedStorage2<RadialGradientPattern> mRadialGradientPattern;
825 : AlignedStorage2<SurfacePattern> mSurfacePattern;
826 : };
827 : Pattern *mPattern;
828 : };
829 :
830 : /* This is an RAII based class that can be used as a drawtarget for
831 : * operations that need a shadow drawn. It will automatically provide a
832 : * temporary target when needed, and if so blend it back with a shadow.
833 : *
834 : * aBounds specifies the bounds of the drawing operation that will be
835 : * drawn to the target, it is given in device space! This function will
836 : * change aBounds to incorporate shadow bounds. If this is NULL the drawing
837 : * operation will be assumed to cover an infinite rect.
838 : */
839 : class AdjustedTarget
840 : {
841 : public:
842 0 : AdjustedTarget(nsCanvasRenderingContext2DAzure *ctx,
843 : mgfx::Rect *aBounds = nsnull)
844 0 : : mCtx(nsnull)
845 : {
846 0 : if (!ctx->NeedToDrawShadow()) {
847 0 : mTarget = ctx->mTarget;
848 0 : return;
849 : }
850 0 : mCtx = ctx;
851 :
852 0 : const ContextState &state = mCtx->CurrentState();
853 :
854 0 : mSigma = state.shadowBlur / 2.0f;
855 :
856 0 : if (mSigma > SIGMA_MAX) {
857 0 : mSigma = SIGMA_MAX;
858 : }
859 :
860 0 : Matrix transform = mCtx->mTarget->GetTransform();
861 :
862 0 : mTempRect = mgfx::Rect(0, 0, ctx->mWidth, ctx->mHeight);
863 :
864 0 : Float blurRadius = mSigma * 3;
865 :
866 : // We need to enlarge and possibly offset our temporary surface
867 : // so that things outside of the canvas may cast shadows.
868 0 : mTempRect.Inflate(Margin(blurRadius + NS_MAX<Float>(state.shadowOffset.x, 0),
869 0 : blurRadius + NS_MAX<Float>(state.shadowOffset.y, 0),
870 0 : blurRadius + NS_MAX<Float>(-state.shadowOffset.x, 0),
871 0 : blurRadius + NS_MAX<Float>(-state.shadowOffset.y, 0)));
872 :
873 0 : if (aBounds) {
874 : // We actually include the bounds of the shadow blur, this makes it
875 : // easier to execute the actual blur on hardware, and shouldn't affect
876 : // the amount of pixels that need to be touched.
877 : aBounds->Inflate(Margin(blurRadius, blurRadius,
878 0 : blurRadius, blurRadius));
879 0 : mTempRect = mTempRect.Intersect(*aBounds);
880 : }
881 :
882 0 : mTempRect.ScaleRoundOut(1.0f);
883 :
884 0 : transform._31 -= mTempRect.x;
885 0 : transform._32 -= mTempRect.y;
886 :
887 : mTarget =
888 0 : mCtx->mTarget->CreateSimilarDrawTarget(IntSize(int32_t(mTempRect.width), int32_t(mTempRect.height)),
889 0 : FORMAT_B8G8R8A8);
890 :
891 0 : if (!mTarget) {
892 : // XXX - Deal with the situation where our temp size is too big to
893 : // fit in a texture.
894 0 : mTarget = ctx->mTarget;
895 0 : mCtx = nsnull;
896 : } else {
897 0 : mTarget->SetTransform(transform);
898 : }
899 : }
900 :
901 0 : ~AdjustedTarget()
902 0 : {
903 0 : if (!mCtx) {
904 : return;
905 : }
906 :
907 0 : RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
908 :
909 0 : mCtx->mTarget->DrawSurfaceWithShadow(snapshot, mTempRect.TopLeft(),
910 0 : Color::FromABGR(mCtx->CurrentState().shadowColor),
911 0 : mCtx->CurrentState().shadowOffset, mSigma,
912 0 : mCtx->CurrentState().op);
913 0 : }
914 :
915 0 : DrawTarget* operator->()
916 : {
917 0 : return mTarget;
918 : }
919 :
920 : private:
921 : RefPtr<DrawTarget> mTarget;
922 : nsCanvasRenderingContext2DAzure *mCtx;
923 : Float mSigma;
924 : mgfx::Rect mTempRect;
925 : };
926 :
927 : nsAutoTArray<ContextState, 3> mStyleStack;
928 :
929 0 : inline ContextState& CurrentState() {
930 0 : return mStyleStack[mStyleStack.Length() - 1];
931 : }
932 :
933 : // other helpers
934 0 : void GetAppUnitsValues(PRUint32 *perDevPixel, PRUint32 *perCSSPixel) {
935 : // If we don't have a canvas element, we just return something generic.
936 0 : PRUint32 devPixel = 60;
937 0 : PRUint32 cssPixel = 60;
938 :
939 0 : nsIPresShell *ps = GetPresShell();
940 : nsPresContext *pc;
941 :
942 0 : if (!ps) goto FINISH;
943 0 : pc = ps->GetPresContext();
944 0 : if (!pc) goto FINISH;
945 0 : devPixel = pc->AppUnitsPerDevPixel();
946 0 : cssPixel = pc->AppUnitsPerCSSPixel();
947 :
948 : FINISH:
949 0 : if (perDevPixel)
950 0 : *perDevPixel = devPixel;
951 0 : if (perCSSPixel)
952 0 : *perCSSPixel = cssPixel;
953 0 : }
954 :
955 : friend struct nsCanvasBidiProcessorAzure;
956 : };
957 :
958 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCanvasRenderingContext2DAzure)
959 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCanvasRenderingContext2DAzure)
960 :
961 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsCanvasRenderingContext2DAzure)
962 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsCanvasRenderingContext2DAzure)
963 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCanvasElement)
964 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
965 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsCanvasRenderingContext2DAzure)
966 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCanvasElement)
967 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
968 :
969 : // XXX
970 : // DOMCI_DATA(CanvasRenderingContext2D, nsCanvasRenderingContext2DAzure)
971 :
972 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCanvasRenderingContext2DAzure)
973 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasRenderingContext2D)
974 0 : NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
975 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMCanvasRenderingContext2D)
976 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CanvasRenderingContext2D)
977 0 : NS_INTERFACE_MAP_END
978 :
979 : /**
980 : ** CanvasRenderingContext2D impl
981 : **/
982 :
983 :
984 : // Initialize our static variables.
985 : PRUint32 nsCanvasRenderingContext2DAzure::sNumLivingContexts = 0;
986 : PRUint8 (*nsCanvasRenderingContext2DAzure::sUnpremultiplyTable)[256] = nsnull;
987 : PRUint8 (*nsCanvasRenderingContext2DAzure::sPremultiplyTable)[256] = nsnull;
988 :
989 : nsresult
990 0 : NS_NewCanvasRenderingContext2DAzure(nsIDOMCanvasRenderingContext2D** aResult)
991 : {
992 : #ifdef XP_WIN
993 : if ((gfxWindowsPlatform::GetPlatform()->GetRenderMode() !=
994 : gfxWindowsPlatform::RENDER_DIRECT2D ||
995 : !gfxWindowsPlatform::GetPlatform()->DWriteEnabled()) &&
996 : !Preferences::GetBool("gfx.canvas.azure.prefer-skia", false)) {
997 : return NS_ERROR_NOT_AVAILABLE;
998 : }
999 : #elif !defined(XP_MACOSX) && !defined(ANDROID) && !defined(LINUX)
1000 : return NS_ERROR_NOT_AVAILABLE;
1001 : #endif
1002 :
1003 0 : nsRefPtr<nsIDOMCanvasRenderingContext2D> ctx = new nsCanvasRenderingContext2DAzure();
1004 0 : if (!ctx)
1005 0 : return NS_ERROR_OUT_OF_MEMORY;
1006 :
1007 0 : *aResult = ctx.forget().get();
1008 0 : return NS_OK;
1009 : }
1010 :
1011 0 : nsCanvasRenderingContext2DAzure::nsCanvasRenderingContext2DAzure()
1012 : : mValid(false), mZero(false), mOpaque(false), mResetLayer(true)
1013 : , mIPC(false)
1014 : , mCanvasElement(nsnull)
1015 : , mIsEntireFrameInvalid(false)
1016 : , mPredictManyRedrawCalls(false), mPathTransformWillUpdate(false)
1017 0 : , mInvalidateCount(0)
1018 : {
1019 0 : sNumLivingContexts++;
1020 0 : }
1021 :
1022 0 : nsCanvasRenderingContext2DAzure::~nsCanvasRenderingContext2DAzure()
1023 : {
1024 0 : Reset();
1025 0 : sNumLivingContexts--;
1026 0 : if (!sNumLivingContexts) {
1027 0 : delete[] sUnpremultiplyTable;
1028 0 : delete[] sPremultiplyTable;
1029 0 : sUnpremultiplyTable = nsnull;
1030 0 : sPremultiplyTable = nsnull;
1031 : }
1032 0 : }
1033 :
1034 : nsresult
1035 0 : nsCanvasRenderingContext2DAzure::Reset()
1036 : {
1037 0 : if (mCanvasElement) {
1038 0 : HTMLCanvasElement()->InvalidateCanvas();
1039 : }
1040 :
1041 : // only do this for non-docshell created contexts,
1042 : // since those are the ones that we created a surface for
1043 0 : if (mValid && !mDocShell) {
1044 0 : gCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
1045 : }
1046 :
1047 0 : mTarget = nsnull;
1048 :
1049 : // Since the target changes the backing texture will change, and this will
1050 : // no longer be valid.
1051 0 : mThebesSurface = nsnull;
1052 0 : mValid = false;
1053 0 : mIsEntireFrameInvalid = false;
1054 0 : mPredictManyRedrawCalls = false;
1055 :
1056 0 : return NS_OK;
1057 : }
1058 :
1059 : nsresult
1060 0 : nsCanvasRenderingContext2DAzure::SetStyleFromStringOrInterface(const nsAString& aStr,
1061 : nsISupports *aInterface,
1062 : Style aWhichStyle)
1063 : {
1064 : nsresult rv;
1065 : nscolor color;
1066 :
1067 0 : if (!aStr.IsVoid()) {
1068 : nsIDocument* document = mCanvasElement ?
1069 0 : HTMLCanvasElement()->OwnerDoc() : nsnull;
1070 :
1071 : // Pass the CSS Loader object to the parser, to allow parser error
1072 : // reports to include the outer window ID.
1073 0 : nsCSSParser parser(document ? document->CSSLoader() : nsnull);
1074 0 : rv = parser.ParseColorString(aStr, nsnull, 0, &color);
1075 0 : if (NS_FAILED(rv)) {
1076 : // Error reporting happens inside the CSS parser
1077 0 : return NS_OK;
1078 : }
1079 :
1080 0 : CurrentState().SetColorStyle(aWhichStyle, color);
1081 0 : return NS_OK;
1082 : }
1083 :
1084 0 : if (aInterface) {
1085 0 : nsCOMPtr<nsCanvasGradientAzure> grad(do_QueryInterface(aInterface));
1086 0 : if (grad) {
1087 0 : CurrentState().SetGradientStyle(aWhichStyle, grad);
1088 0 : return NS_OK;
1089 : }
1090 :
1091 0 : nsCOMPtr<nsCanvasPatternAzure> pattern(do_QueryInterface(aInterface));
1092 0 : if (pattern) {
1093 0 : CurrentState().SetPatternStyle(aWhichStyle, pattern);
1094 0 : return NS_OK;
1095 : }
1096 : }
1097 :
1098 : nsContentUtils::ReportToConsole(
1099 : nsIScriptError::warningFlag,
1100 : "Canvas",
1101 0 : mCanvasElement ? HTMLCanvasElement()->OwnerDoc() : nsnull,
1102 : nsContentUtils::eDOM_PROPERTIES,
1103 0 : "UnexpectedCanvasVariantStyle");
1104 :
1105 0 : return NS_OK;
1106 : }
1107 :
1108 : nsresult
1109 0 : nsCanvasRenderingContext2DAzure::GetStyleAsStringOrInterface(nsAString& aStr,
1110 : nsISupports **aInterface,
1111 : PRInt32 *aType,
1112 : Style aWhichStyle)
1113 : {
1114 0 : const ContextState &state = CurrentState();
1115 :
1116 0 : if (state.patternStyles[aWhichStyle]) {
1117 0 : aStr.SetIsVoid(true);
1118 0 : NS_ADDREF(*aInterface = state.patternStyles[aWhichStyle]);
1119 0 : *aType = CMG_STYLE_PATTERN;
1120 0 : } else if (state.gradientStyles[aWhichStyle]) {
1121 0 : aStr.SetIsVoid(true);
1122 0 : NS_ADDREF(*aInterface = state.gradientStyles[aWhichStyle]);
1123 0 : *aType = CMG_STYLE_GRADIENT;
1124 : } else {
1125 0 : StyleColorToString(state.colorStyles[aWhichStyle], aStr);
1126 0 : *aInterface = nsnull;
1127 0 : *aType = CMG_STYLE_STRING;
1128 : }
1129 :
1130 0 : return NS_OK;
1131 : }
1132 :
1133 : void
1134 0 : nsCanvasRenderingContext2DAzure::StyleColorToString(const nscolor& aColor, nsAString& aStr)
1135 : {
1136 : // We can't reuse the normal CSS color stringification code,
1137 : // because the spec calls for a different algorithm for canvas.
1138 0 : if (NS_GET_A(aColor) == 255) {
1139 : CopyUTF8toUTF16(nsPrintfCString(100, "#%02x%02x%02x",
1140 : NS_GET_R(aColor),
1141 : NS_GET_G(aColor),
1142 0 : NS_GET_B(aColor)),
1143 0 : aStr);
1144 : } else {
1145 : CopyUTF8toUTF16(nsPrintfCString(100, "rgba(%d, %d, %d, ",
1146 : NS_GET_R(aColor),
1147 : NS_GET_G(aColor),
1148 0 : NS_GET_B(aColor)),
1149 0 : aStr);
1150 0 : aStr.AppendFloat(nsStyleUtil::ColorComponentToFloat(NS_GET_A(aColor)));
1151 0 : aStr.Append(')');
1152 : }
1153 0 : }
1154 :
1155 : nsresult
1156 0 : nsCanvasRenderingContext2DAzure::Redraw()
1157 : {
1158 0 : if (mIsEntireFrameInvalid) {
1159 0 : return NS_OK;
1160 : }
1161 :
1162 0 : mIsEntireFrameInvalid = true;
1163 :
1164 0 : if (!mCanvasElement) {
1165 0 : NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
1166 0 : return NS_OK;
1167 : }
1168 :
1169 0 : if (!mThebesSurface)
1170 : mThebesSurface =
1171 0 : gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
1172 0 : mThebesSurface->MarkDirty();
1173 :
1174 0 : nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement());
1175 :
1176 0 : HTMLCanvasElement()->InvalidateCanvasContent(nsnull);
1177 :
1178 0 : return NS_OK;
1179 : }
1180 :
1181 : void
1182 0 : nsCanvasRenderingContext2DAzure::Redraw(const mgfx::Rect &r)
1183 : {
1184 0 : ++mInvalidateCount;
1185 :
1186 0 : if (mIsEntireFrameInvalid) {
1187 0 : return;
1188 : }
1189 :
1190 0 : if (mPredictManyRedrawCalls ||
1191 : mInvalidateCount > kCanvasMaxInvalidateCount) {
1192 0 : Redraw();
1193 0 : return;
1194 : }
1195 :
1196 0 : if (!mCanvasElement) {
1197 0 : NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
1198 0 : return;
1199 : }
1200 :
1201 0 : if (!mThebesSurface)
1202 : mThebesSurface =
1203 0 : gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
1204 0 : mThebesSurface->MarkDirty();
1205 :
1206 0 : nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement());
1207 :
1208 0 : gfxRect tmpR = ThebesRect(r);
1209 0 : HTMLCanvasElement()->InvalidateCanvasContent(&tmpR);
1210 :
1211 0 : return;
1212 : }
1213 :
1214 : nsresult
1215 0 : nsCanvasRenderingContext2DAzure::RedrawUser(const gfxRect& r)
1216 : {
1217 0 : if (mIsEntireFrameInvalid) {
1218 0 : ++mInvalidateCount;
1219 0 : return NS_OK;
1220 : }
1221 :
1222 : mgfx::Rect newr =
1223 0 : mTarget->GetTransform().TransformBounds(ToRect(r));
1224 0 : Redraw(newr);
1225 :
1226 0 : return NS_OK;
1227 : }
1228 :
1229 : NS_IMETHODIMP
1230 0 : nsCanvasRenderingContext2DAzure::SetDimensions(PRInt32 width, PRInt32 height)
1231 : {
1232 0 : RefPtr<DrawTarget> target;
1233 :
1234 : // Zero sized surfaces cause issues, so just go with 1x1.
1235 0 : if (height == 0 || width == 0) {
1236 0 : mZero = true;
1237 0 : height = 1;
1238 0 : width = 1;
1239 : } else {
1240 0 : mZero = false;
1241 : }
1242 :
1243 : // Check that the dimensions are sane
1244 0 : IntSize size(width, height);
1245 0 : if (size.width <= 0xFFFF && size.height <= 0xFFFF &&
1246 : size.width >= 0 && size.height >= 0) {
1247 0 : SurfaceFormat format = GetSurfaceFormat();
1248 0 : nsCOMPtr<nsIContent> content = do_QueryObject(mCanvasElement);
1249 0 : nsIDocument* ownerDoc = nsnull;
1250 0 : if (content) {
1251 0 : ownerDoc = content->OwnerDoc();
1252 : }
1253 :
1254 0 : nsRefPtr<LayerManager> layerManager = nsnull;
1255 :
1256 0 : if (ownerDoc) {
1257 : layerManager =
1258 0 : nsContentUtils::PersistentLayerManagerForDocument(ownerDoc);
1259 : }
1260 :
1261 0 : if (layerManager) {
1262 0 : target = layerManager->CreateDrawTarget(size, format);
1263 : } else {
1264 0 : target = gfxPlatform::GetPlatform()->CreateOffscreenDrawTarget(size, format);
1265 : }
1266 : }
1267 :
1268 0 : if (target) {
1269 0 : if (gCanvasAzureMemoryReporter == nsnull) {
1270 0 : gCanvasAzureMemoryReporter = new NS_MEMORY_REPORTER_NAME(CanvasAzureMemory);
1271 0 : NS_RegisterMemoryReporter(gCanvasAzureMemoryReporter);
1272 : }
1273 :
1274 0 : gCanvasAzureMemoryUsed += width * height * 4;
1275 0 : JSContext* context = nsContentUtils::GetCurrentJSContext();
1276 0 : if (context) {
1277 0 : JS_updateMallocCounter(context, width * height * 4);
1278 : }
1279 : }
1280 :
1281 0 : return InitializeWithTarget(target, width, height);
1282 : }
1283 :
1284 : nsresult
1285 0 : nsCanvasRenderingContext2DAzure::InitializeWithTarget(DrawTarget *target, PRInt32 width, PRInt32 height)
1286 : {
1287 0 : Reset();
1288 :
1289 0 : NS_ASSERTION(mCanvasElement, "Must have a canvas element!");
1290 0 : mDocShell = nsnull;
1291 :
1292 0 : mWidth = width;
1293 0 : mHeight = height;
1294 :
1295 : // This first time this is called on this object is via
1296 : // nsHTMLCanvasElement::GetContext. If target was non-null then mTarget is
1297 : // non-null, otherwise we'll return an error here and GetContext won't
1298 : // return this context object and we'll never enter this code again.
1299 : // All other times this method is called, if target is null then
1300 : // mTarget won't be changed, i.e. it will remain non-null, or else it
1301 : // will be set to non-null.
1302 : // In all cases, any usable canvas context will have non-null mTarget.
1303 :
1304 0 : if (target) {
1305 0 : mValid = true;
1306 0 : mTarget = target;
1307 : } else {
1308 0 : mValid = false;
1309 : // Create a dummy target in the hopes that it will help us deal with users
1310 : // calling into us after having changed the size where the size resulted
1311 : // in an inability to create a correct DrawTarget.
1312 0 : mTarget = gfxPlatform::GetPlatform()->CreateOffscreenDrawTarget(IntSize(1, 1), FORMAT_B8G8R8A8);
1313 : }
1314 :
1315 0 : mResetLayer = true;
1316 :
1317 : // set up the initial canvas defaults
1318 0 : mStyleStack.Clear();
1319 0 : mPathBuilder = nsnull;
1320 0 : mPath = nsnull;
1321 0 : mDSPathBuilder = nsnull;
1322 :
1323 0 : ContextState *state = mStyleStack.AppendElement();
1324 0 : state->globalAlpha = 1.0;
1325 :
1326 0 : state->colorStyles[STYLE_FILL] = NS_RGB(0,0,0);
1327 0 : state->colorStyles[STYLE_STROKE] = NS_RGB(0,0,0);
1328 0 : state->shadowColor = NS_RGBA(0,0,0,0);
1329 :
1330 0 : if (mTarget) {
1331 0 : mTarget->ClearRect(mgfx::Rect(Point(0, 0), Size(mWidth, mHeight)));
1332 : // always force a redraw, because if the surface dimensions were reset
1333 : // then the surface became cleared, and we need to redraw everything.
1334 0 : Redraw();
1335 : }
1336 :
1337 0 : return mValid ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
1338 : }
1339 :
1340 : NS_IMETHODIMP
1341 0 : nsCanvasRenderingContext2DAzure::SetIsOpaque(bool isOpaque)
1342 : {
1343 0 : if (isOpaque == mOpaque)
1344 0 : return NS_OK;
1345 :
1346 0 : mOpaque = isOpaque;
1347 :
1348 0 : if (mValid) {
1349 : /* If we've already been created, let SetDimensions take care of
1350 : * recreating our surface
1351 : */
1352 0 : return SetDimensions(mWidth, mHeight);
1353 : }
1354 :
1355 0 : return NS_OK;
1356 : }
1357 :
1358 : NS_IMETHODIMP
1359 0 : nsCanvasRenderingContext2DAzure::SetIsIPC(bool isIPC)
1360 : {
1361 0 : if (isIPC == mIPC)
1362 0 : return NS_OK;
1363 :
1364 0 : mIPC = isIPC;
1365 :
1366 0 : if (mValid) {
1367 : /* If we've already been created, let SetDimensions take care of
1368 : * recreating our surface
1369 : */
1370 0 : return SetDimensions(mWidth, mHeight);
1371 : }
1372 :
1373 0 : return NS_OK;
1374 : }
1375 :
1376 : NS_IMETHODIMP
1377 0 : nsCanvasRenderingContext2DAzure::Render(gfxContext *ctx, gfxPattern::GraphicsFilter aFilter)
1378 : {
1379 0 : nsresult rv = NS_OK;
1380 :
1381 0 : if (!mValid || !mTarget) {
1382 0 : return NS_ERROR_FAILURE;
1383 : }
1384 :
1385 0 : nsRefPtr<gfxASurface> surface;
1386 :
1387 0 : if (NS_FAILED(GetThebesSurface(getter_AddRefs(surface)))) {
1388 0 : return NS_ERROR_FAILURE;
1389 : }
1390 :
1391 0 : nsRefPtr<gfxPattern> pat = new gfxPattern(surface);
1392 :
1393 0 : pat->SetFilter(aFilter);
1394 0 : pat->SetExtend(gfxPattern::EXTEND_PAD);
1395 :
1396 0 : gfxContext::GraphicsOperator op = ctx->CurrentOperator();
1397 0 : if (mOpaque)
1398 0 : ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
1399 :
1400 : // XXX I don't want to use PixelSnapped here, but layout doesn't guarantee
1401 : // pixel alignment for this stuff!
1402 0 : ctx->NewPath();
1403 0 : ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, mWidth, mHeight), pat);
1404 0 : ctx->Fill();
1405 :
1406 0 : if (mOpaque)
1407 0 : ctx->SetOperator(op);
1408 :
1409 0 : return rv;
1410 : }
1411 :
1412 : NS_IMETHODIMP
1413 0 : nsCanvasRenderingContext2DAzure::GetInputStream(const char *aMimeType,
1414 : const PRUnichar *aEncoderOptions,
1415 : nsIInputStream **aStream)
1416 : {
1417 0 : if (!mValid || !mTarget) {
1418 0 : return NS_ERROR_FAILURE;
1419 : }
1420 :
1421 0 : nsRefPtr<gfxASurface> surface;
1422 :
1423 0 : if (NS_FAILED(GetThebesSurface(getter_AddRefs(surface)))) {
1424 0 : return NS_ERROR_FAILURE;
1425 : }
1426 :
1427 : nsresult rv;
1428 0 : const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
1429 0 : nsAutoArrayPtr<char> conid(new (std::nothrow) char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
1430 :
1431 0 : if (!conid) {
1432 0 : return NS_ERROR_OUT_OF_MEMORY;
1433 : }
1434 :
1435 0 : strcpy(conid, encoderPrefix);
1436 0 : strcat(conid, aMimeType);
1437 :
1438 0 : nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
1439 0 : if (!encoder) {
1440 0 : return NS_ERROR_FAILURE;
1441 : }
1442 :
1443 0 : nsAutoArrayPtr<PRUint8> imageBuffer(new (std::nothrow) PRUint8[mWidth * mHeight * 4]);
1444 0 : if (!imageBuffer) {
1445 0 : return NS_ERROR_OUT_OF_MEMORY;
1446 : }
1447 :
1448 : nsRefPtr<gfxImageSurface> imgsurf =
1449 : new gfxImageSurface(imageBuffer.get(),
1450 : gfxIntSize(mWidth, mHeight),
1451 : mWidth * 4,
1452 0 : gfxASurface::ImageFormatARGB32);
1453 :
1454 0 : if (!imgsurf || imgsurf->CairoStatus()) {
1455 0 : return NS_ERROR_FAILURE;
1456 : }
1457 :
1458 0 : nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
1459 :
1460 0 : if (!ctx || ctx->HasError()) {
1461 0 : return NS_ERROR_FAILURE;
1462 : }
1463 :
1464 0 : ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
1465 0 : ctx->SetSource(surface, gfxPoint(0, 0));
1466 0 : ctx->Paint();
1467 :
1468 0 : rv = encoder->InitFromData(imageBuffer.get(),
1469 : mWidth * mHeight * 4, mWidth, mHeight, mWidth * 4,
1470 : imgIEncoder::INPUT_FORMAT_HOSTARGB,
1471 0 : nsDependentString(aEncoderOptions));
1472 0 : NS_ENSURE_SUCCESS(rv, rv);
1473 :
1474 0 : return CallQueryInterface(encoder, aStream);
1475 : }
1476 :
1477 : SurfaceFormat
1478 0 : nsCanvasRenderingContext2DAzure::GetSurfaceFormat() const
1479 : {
1480 0 : return mOpaque ? FORMAT_B8G8R8X8 : FORMAT_B8G8R8A8;
1481 : }
1482 :
1483 : //
1484 : // nsCanvasRenderingContext2DAzure impl
1485 : //
1486 :
1487 : NS_IMETHODIMP
1488 0 : nsCanvasRenderingContext2DAzure::SetCanvasElement(nsHTMLCanvasElement* aCanvasElement)
1489 : {
1490 0 : mCanvasElement = aCanvasElement;
1491 :
1492 0 : return NS_OK;
1493 : }
1494 :
1495 : NS_IMETHODIMP
1496 0 : nsCanvasRenderingContext2DAzure::GetCanvas(nsIDOMHTMLCanvasElement **canvas)
1497 : {
1498 0 : NS_IF_ADDREF(*canvas = mCanvasElement);
1499 :
1500 0 : return NS_OK;
1501 : }
1502 :
1503 : //
1504 : // state
1505 : //
1506 :
1507 : NS_IMETHODIMP
1508 0 : nsCanvasRenderingContext2DAzure::Save()
1509 : {
1510 0 : mStyleStack[mStyleStack.Length() - 1].transform = mTarget->GetTransform();
1511 0 : mStyleStack.SetCapacity(mStyleStack.Length() + 1);
1512 0 : mStyleStack.AppendElement(CurrentState());
1513 0 : return NS_OK;
1514 : }
1515 :
1516 : NS_IMETHODIMP
1517 0 : nsCanvasRenderingContext2DAzure::Restore()
1518 : {
1519 0 : if (mStyleStack.Length() - 1 == 0)
1520 0 : return NS_OK;
1521 :
1522 0 : for (PRUint32 i = 0; i < CurrentState().clipsPushed.size(); i++) {
1523 0 : mTarget->PopClip();
1524 : }
1525 :
1526 0 : mStyleStack.RemoveElementAt(mStyleStack.Length() - 1);
1527 :
1528 0 : TransformWillUpdate();
1529 :
1530 0 : mTarget->SetTransform(CurrentState().transform);
1531 :
1532 0 : return NS_OK;
1533 : }
1534 :
1535 : //
1536 : // transformations
1537 : //
1538 :
1539 : NS_IMETHODIMP
1540 0 : nsCanvasRenderingContext2DAzure::Scale(float x, float y)
1541 : {
1542 0 : if (!FloatValidate(x,y))
1543 0 : return NS_OK;
1544 :
1545 0 : TransformWillUpdate();
1546 :
1547 0 : Matrix newMatrix = mTarget->GetTransform();
1548 0 : mTarget->SetTransform(newMatrix.Scale(x, y));
1549 0 : return NS_OK;
1550 : }
1551 :
1552 : NS_IMETHODIMP
1553 0 : nsCanvasRenderingContext2DAzure::Rotate(float angle)
1554 : {
1555 0 : if (!FloatValidate(angle))
1556 0 : return NS_OK;
1557 :
1558 0 : TransformWillUpdate();
1559 :
1560 0 : Matrix rotation = Matrix::Rotation(angle);
1561 0 : mTarget->SetTransform(rotation * mTarget->GetTransform());
1562 0 : return NS_OK;
1563 : }
1564 :
1565 : NS_IMETHODIMP
1566 0 : nsCanvasRenderingContext2DAzure::Translate(float x, float y)
1567 : {
1568 0 : if (!FloatValidate(x,y)) {
1569 0 : return NS_OK;
1570 : }
1571 :
1572 0 : TransformWillUpdate();
1573 :
1574 0 : Matrix newMatrix = mTarget->GetTransform();
1575 0 : mTarget->SetTransform(newMatrix.Translate(x, y));
1576 0 : return NS_OK;
1577 : }
1578 :
1579 : NS_IMETHODIMP
1580 0 : nsCanvasRenderingContext2DAzure::Transform(float m11, float m12, float m21, float m22, float dx, float dy)
1581 : {
1582 0 : if (!FloatValidate(m11,m12,m21,m22,dx,dy)) {
1583 0 : return NS_OK;
1584 : }
1585 :
1586 0 : TransformWillUpdate();
1587 :
1588 0 : Matrix matrix(m11, m12, m21, m22, dx, dy);
1589 0 : mTarget->SetTransform(matrix * mTarget->GetTransform());
1590 0 : return NS_OK;
1591 : }
1592 :
1593 : NS_IMETHODIMP
1594 0 : nsCanvasRenderingContext2DAzure::SetTransform(float m11, float m12, float m21, float m22, float dx, float dy)
1595 : {
1596 0 : if (!FloatValidate(m11,m12,m21,m22,dx,dy)) {
1597 0 : return NS_OK;
1598 : }
1599 :
1600 0 : TransformWillUpdate();
1601 :
1602 0 : Matrix matrix(m11, m12, m21, m22, dx, dy);
1603 0 : mTarget->SetTransform(matrix);
1604 :
1605 0 : return NS_OK;
1606 : }
1607 :
1608 : NS_IMETHODIMP
1609 0 : nsCanvasRenderingContext2DAzure::SetMozCurrentTransform(JSContext* cx,
1610 : const jsval& matrix)
1611 : {
1612 : nsresult rv;
1613 0 : Matrix newCTM;
1614 :
1615 0 : if (!JSValToMatrix(cx, matrix, &newCTM, &rv)) {
1616 0 : return rv;
1617 : }
1618 :
1619 0 : mTarget->SetTransform(newCTM);
1620 :
1621 0 : return NS_OK;
1622 : }
1623 :
1624 : NS_IMETHODIMP
1625 0 : nsCanvasRenderingContext2DAzure::GetMozCurrentTransform(JSContext* cx,
1626 : jsval* matrix)
1627 : {
1628 0 : return MatrixToJSVal(mTarget->GetTransform(), cx, matrix);
1629 : }
1630 :
1631 : NS_IMETHODIMP
1632 0 : nsCanvasRenderingContext2DAzure::SetMozCurrentTransformInverse(JSContext* cx,
1633 : const jsval& matrix)
1634 : {
1635 : nsresult rv;
1636 0 : Matrix newCTMInverse;
1637 :
1638 0 : if (!JSValToMatrix(cx, matrix, &newCTMInverse, &rv)) {
1639 0 : return rv;
1640 : }
1641 :
1642 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
1643 0 : if (newCTMInverse.Invert()) {
1644 0 : mTarget->SetTransform(newCTMInverse);
1645 : }
1646 :
1647 0 : return NS_OK;
1648 : }
1649 :
1650 : NS_IMETHODIMP
1651 0 : nsCanvasRenderingContext2DAzure::GetMozCurrentTransformInverse(JSContext* cx,
1652 : jsval* matrix)
1653 : {
1654 0 : Matrix ctm = mTarget->GetTransform();
1655 :
1656 0 : if (!ctm.Invert()) {
1657 0 : double NaN = JSVAL_TO_DOUBLE(JS_GetNaNValue(cx));
1658 0 : ctm = Matrix(NaN, NaN, NaN, NaN, NaN, NaN);
1659 : }
1660 :
1661 0 : return MatrixToJSVal(ctm, cx, matrix);
1662 : }
1663 :
1664 : //
1665 : // colors
1666 : //
1667 :
1668 : NS_IMETHODIMP
1669 0 : nsCanvasRenderingContext2DAzure::SetGlobalAlpha(float aGlobalAlpha)
1670 : {
1671 0 : if (!FloatValidate(aGlobalAlpha) || aGlobalAlpha < 0.0 || aGlobalAlpha > 1.0) {
1672 0 : return NS_OK;
1673 : }
1674 :
1675 0 : CurrentState().globalAlpha = aGlobalAlpha;
1676 :
1677 0 : return NS_OK;
1678 : }
1679 :
1680 : NS_IMETHODIMP
1681 0 : nsCanvasRenderingContext2DAzure::GetGlobalAlpha(float *aGlobalAlpha)
1682 : {
1683 0 : *aGlobalAlpha = CurrentState().globalAlpha;
1684 0 : return NS_OK;
1685 : }
1686 :
1687 : NS_IMETHODIMP
1688 0 : nsCanvasRenderingContext2DAzure::SetStrokeStyle(nsIVariant *aValue)
1689 : {
1690 0 : if (!aValue)
1691 0 : return NS_ERROR_FAILURE;
1692 :
1693 0 : nsString str;
1694 :
1695 : nsresult rv;
1696 : PRUint16 vtype;
1697 0 : rv = aValue->GetDataType(&vtype);
1698 0 : NS_ENSURE_SUCCESS(rv, rv);
1699 :
1700 0 : if (vtype == nsIDataType::VTYPE_INTERFACE ||
1701 : vtype == nsIDataType::VTYPE_INTERFACE_IS)
1702 : {
1703 : nsIID *iid;
1704 0 : nsCOMPtr<nsISupports> sup;
1705 0 : rv = aValue->GetAsInterface(&iid, getter_AddRefs(sup));
1706 0 : NS_ENSURE_SUCCESS(rv, rv);
1707 0 : if (iid) {
1708 0 : NS_Free(iid);
1709 : }
1710 :
1711 0 : str.SetIsVoid(true);
1712 0 : return SetStrokeStyle_multi(str, sup);
1713 : }
1714 :
1715 0 : rv = aValue->GetAsAString(str);
1716 0 : NS_ENSURE_SUCCESS(rv, rv);
1717 :
1718 0 : return SetStrokeStyle_multi(str, nsnull);
1719 : }
1720 :
1721 : NS_IMETHODIMP
1722 0 : nsCanvasRenderingContext2DAzure::GetStrokeStyle(nsIVariant **aResult)
1723 : {
1724 0 : nsCOMPtr<nsIWritableVariant> wv = do_CreateInstance(NS_VARIANT_CONTRACTID);
1725 :
1726 0 : nsCOMPtr<nsISupports> sup;
1727 0 : nsString str;
1728 : PRInt32 t;
1729 0 : nsresult rv = GetStrokeStyle_multi(str, getter_AddRefs(sup), &t);
1730 0 : NS_ENSURE_SUCCESS(rv, rv);
1731 :
1732 0 : if (t == CMG_STYLE_STRING) {
1733 0 : rv = wv->SetAsAString(str);
1734 0 : } else if (t == CMG_STYLE_PATTERN) {
1735 0 : rv = wv->SetAsInterface(NS_GET_IID(nsIDOMCanvasPattern),
1736 0 : sup);
1737 0 : } else if (t == CMG_STYLE_GRADIENT) {
1738 0 : rv = wv->SetAsInterface(NS_GET_IID(nsIDOMCanvasGradient),
1739 0 : sup);
1740 : } else {
1741 0 : NS_ERROR("Unknown type from GetStroke/FillStyle_multi!");
1742 0 : return NS_ERROR_FAILURE;
1743 : }
1744 0 : NS_ENSURE_SUCCESS(rv, rv);
1745 :
1746 0 : NS_IF_ADDREF(*aResult = wv.get());
1747 0 : return NS_OK;
1748 : }
1749 :
1750 : NS_IMETHODIMP
1751 0 : nsCanvasRenderingContext2DAzure::SetFillStyle(nsIVariant *aValue)
1752 : {
1753 0 : if (!aValue) {
1754 0 : return NS_ERROR_FAILURE;
1755 : }
1756 :
1757 0 : nsString str;
1758 : nsresult rv;
1759 : PRUint16 vtype;
1760 0 : rv = aValue->GetDataType(&vtype);
1761 0 : NS_ENSURE_SUCCESS(rv, rv);
1762 :
1763 0 : if (vtype == nsIDataType::VTYPE_INTERFACE ||
1764 : vtype == nsIDataType::VTYPE_INTERFACE_IS)
1765 : {
1766 : nsIID *iid;
1767 0 : nsCOMPtr<nsISupports> sup;
1768 0 : rv = aValue->GetAsInterface(&iid, getter_AddRefs(sup));
1769 0 : NS_ENSURE_SUCCESS(rv, rv);
1770 :
1771 0 : str.SetIsVoid(true);
1772 0 : return SetFillStyle_multi(str, sup);
1773 : }
1774 :
1775 0 : rv = aValue->GetAsAString(str);
1776 0 : NS_ENSURE_SUCCESS(rv, rv);
1777 :
1778 0 : return SetFillStyle_multi(str, nsnull);
1779 : }
1780 :
1781 : NS_IMETHODIMP
1782 0 : nsCanvasRenderingContext2DAzure::GetFillStyle(nsIVariant **aResult)
1783 : {
1784 0 : nsCOMPtr<nsIWritableVariant> wv = do_CreateInstance(NS_VARIANT_CONTRACTID);
1785 :
1786 0 : nsCOMPtr<nsISupports> sup;
1787 0 : nsString str;
1788 : PRInt32 t;
1789 0 : nsresult rv = GetFillStyle_multi(str, getter_AddRefs(sup), &t);
1790 0 : NS_ENSURE_SUCCESS(rv, rv);
1791 :
1792 0 : if (t == CMG_STYLE_STRING) {
1793 0 : rv = wv->SetAsAString(str);
1794 0 : } else if (t == CMG_STYLE_PATTERN) {
1795 0 : rv = wv->SetAsInterface(NS_GET_IID(nsIDOMCanvasPattern),
1796 0 : sup);
1797 0 : } else if (t == CMG_STYLE_GRADIENT) {
1798 0 : rv = wv->SetAsInterface(NS_GET_IID(nsIDOMCanvasGradient),
1799 0 : sup);
1800 : } else {
1801 0 : NS_ERROR("Unknown type from GetStroke/FillStyle_multi!");
1802 0 : return NS_ERROR_FAILURE;
1803 : }
1804 0 : NS_ENSURE_SUCCESS(rv, rv);
1805 :
1806 0 : NS_IF_ADDREF(*aResult = wv.get());
1807 0 : return NS_OK;
1808 : }
1809 :
1810 : NS_IMETHODIMP
1811 0 : nsCanvasRenderingContext2DAzure::SetMozFillRule(const nsAString& aString)
1812 : {
1813 : FillRule rule;
1814 :
1815 0 : if (aString.EqualsLiteral("evenodd"))
1816 0 : rule = FILL_EVEN_ODD;
1817 0 : else if (aString.EqualsLiteral("nonzero"))
1818 0 : rule = FILL_WINDING;
1819 : else
1820 0 : return NS_OK;
1821 :
1822 0 : CurrentState().fillRule = rule;
1823 0 : return NS_OK;
1824 : }
1825 :
1826 : NS_IMETHODIMP
1827 0 : nsCanvasRenderingContext2DAzure::GetMozFillRule(nsAString& aString)
1828 : {
1829 0 : switch (CurrentState().fillRule) {
1830 : case FILL_WINDING:
1831 0 : aString.AssignLiteral("nonzero"); break;
1832 : case FILL_EVEN_ODD:
1833 0 : aString.AssignLiteral("evenodd"); break;
1834 : }
1835 :
1836 0 : return NS_OK;
1837 : }
1838 :
1839 : NS_IMETHODIMP
1840 0 : nsCanvasRenderingContext2DAzure::SetStrokeStyle_multi(const nsAString& aStr, nsISupports *aInterface)
1841 : {
1842 0 : return SetStyleFromStringOrInterface(aStr, aInterface, STYLE_STROKE);
1843 : }
1844 :
1845 : NS_IMETHODIMP
1846 0 : nsCanvasRenderingContext2DAzure::GetStrokeStyle_multi(nsAString& aStr, nsISupports **aInterface, PRInt32 *aType)
1847 : {
1848 0 : return GetStyleAsStringOrInterface(aStr, aInterface, aType, STYLE_STROKE);
1849 : }
1850 :
1851 : NS_IMETHODIMP
1852 0 : nsCanvasRenderingContext2DAzure::SetFillStyle_multi(const nsAString& aStr, nsISupports *aInterface)
1853 : {
1854 0 : return SetStyleFromStringOrInterface(aStr, aInterface, STYLE_FILL);
1855 : }
1856 :
1857 : NS_IMETHODIMP
1858 0 : nsCanvasRenderingContext2DAzure::GetFillStyle_multi(nsAString& aStr, nsISupports **aInterface, PRInt32 *aType)
1859 : {
1860 0 : return GetStyleAsStringOrInterface(aStr, aInterface, aType, STYLE_FILL);
1861 : }
1862 :
1863 : //
1864 : // gradients and patterns
1865 : //
1866 : NS_IMETHODIMP
1867 0 : nsCanvasRenderingContext2DAzure::CreateLinearGradient(float x0, float y0, float x1, float y1,
1868 : nsIDOMCanvasGradient **_retval)
1869 : {
1870 0 : if (!FloatValidate(x0,y0,x1,y1)) {
1871 0 : return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1872 : }
1873 :
1874 : nsRefPtr<nsIDOMCanvasGradient> grad =
1875 0 : new nsCanvasLinearGradientAzure(Point(x0, y0), Point(x1, y1));
1876 :
1877 0 : *_retval = grad.forget().get();
1878 0 : return NS_OK;
1879 : }
1880 :
1881 : NS_IMETHODIMP
1882 0 : nsCanvasRenderingContext2DAzure::CreateRadialGradient(float x0, float y0, float r0,
1883 : float x1, float y1, float r1,
1884 : nsIDOMCanvasGradient **_retval)
1885 : {
1886 0 : if (!FloatValidate(x0,y0,r0,x1,y1,r1)) {
1887 0 : return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1888 : }
1889 :
1890 0 : if (r0 < 0.0 || r1 < 0.0) {
1891 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
1892 : }
1893 :
1894 : nsRefPtr<nsIDOMCanvasGradient> grad =
1895 0 : new nsCanvasRadialGradientAzure(Point(x0, y0), r0, Point(x1, y1), r1);
1896 :
1897 0 : *_retval = grad.forget().get();
1898 0 : return NS_OK;
1899 : }
1900 :
1901 : NS_IMETHODIMP
1902 0 : nsCanvasRenderingContext2DAzure::CreatePattern(nsIDOMHTMLElement *image,
1903 : const nsAString& repeat,
1904 : nsIDOMCanvasPattern **_retval)
1905 : {
1906 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(image);
1907 0 : if (!content) {
1908 0 : return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
1909 : }
1910 :
1911 : nsCanvasPatternAzure::RepeatMode repeatMode =
1912 0 : nsCanvasPatternAzure::NOREPEAT;
1913 :
1914 0 : if (repeat.IsEmpty() || repeat.EqualsLiteral("repeat")) {
1915 0 : repeatMode = nsCanvasPatternAzure::REPEAT;
1916 0 : } else if (repeat.EqualsLiteral("repeat-x")) {
1917 0 : repeatMode = nsCanvasPatternAzure::REPEATX;
1918 0 : } else if (repeat.EqualsLiteral("repeat-y")) {
1919 0 : repeatMode = nsCanvasPatternAzure::REPEATY;
1920 0 : } else if (repeat.EqualsLiteral("no-repeat")) {
1921 0 : repeatMode = nsCanvasPatternAzure::NOREPEAT;
1922 : } else {
1923 0 : return NS_ERROR_DOM_SYNTAX_ERR;
1924 : }
1925 :
1926 0 : nsHTMLCanvasElement* canvas = nsHTMLCanvasElement::FromContent(content);
1927 0 : if (canvas) {
1928 0 : nsIntSize size = canvas->GetSize();
1929 0 : if (size.width == 0 || size.height == 0) {
1930 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
1931 : }
1932 :
1933 : // Special case for Canvas, which could be an Azure canvas!
1934 0 : nsICanvasRenderingContextInternal *srcCanvas = canvas->GetContextAtIndex(0);
1935 0 : if (srcCanvas) {
1936 : // This might not be an Azure canvas!
1937 0 : RefPtr<SourceSurface> srcSurf = srcCanvas->GetSurfaceSnapshot();
1938 :
1939 : nsRefPtr<nsCanvasPatternAzure> pat =
1940 0 : new nsCanvasPatternAzure(srcSurf, repeatMode, content->NodePrincipal(), canvas->IsWriteOnly(), false);
1941 :
1942 0 : *_retval = pat.forget().get();
1943 0 : return NS_OK;
1944 : }
1945 : }
1946 :
1947 : // The canvas spec says that createPattern should use the first frame
1948 : // of animated images
1949 : nsLayoutUtils::SurfaceFromElementResult res =
1950 0 : nsLayoutUtils::SurfaceFromElement(content->AsElement(),
1951 0 : nsLayoutUtils::SFE_WANT_FIRST_FRAME | nsLayoutUtils::SFE_WANT_NEW_SURFACE);
1952 :
1953 0 : if (!res.mSurface) {
1954 0 : return NS_ERROR_NOT_AVAILABLE;
1955 : }
1956 :
1957 : // Ignore nsnull cairo surfaces! See bug 666312.
1958 0 : if (!res.mSurface->CairoSurface()) {
1959 0 : return NS_OK;
1960 : }
1961 :
1962 : RefPtr<SourceSurface> srcSurf =
1963 0 : gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mTarget, res.mSurface);
1964 :
1965 : nsRefPtr<nsCanvasPatternAzure> pat =
1966 : new nsCanvasPatternAzure(srcSurf, repeatMode, res.mPrincipal,
1967 0 : res.mIsWriteOnly, res.mCORSUsed);
1968 :
1969 0 : *_retval = pat.forget().get();
1970 0 : return NS_OK;
1971 : }
1972 :
1973 : //
1974 : // shadows
1975 : //
1976 : NS_IMETHODIMP
1977 0 : nsCanvasRenderingContext2DAzure::SetShadowOffsetX(float x)
1978 : {
1979 0 : if (!FloatValidate(x)) {
1980 0 : return NS_OK;
1981 : }
1982 :
1983 0 : CurrentState().shadowOffset.x = x;
1984 0 : return NS_OK;
1985 : }
1986 :
1987 : NS_IMETHODIMP
1988 0 : nsCanvasRenderingContext2DAzure::GetShadowOffsetX(float *x)
1989 : {
1990 0 : *x = static_cast<float>(CurrentState().shadowOffset.x);
1991 0 : return NS_OK;
1992 : }
1993 :
1994 : NS_IMETHODIMP
1995 0 : nsCanvasRenderingContext2DAzure::SetShadowOffsetY(float y)
1996 : {
1997 0 : if (!FloatValidate(y)) {
1998 0 : return NS_OK;
1999 : }
2000 :
2001 0 : CurrentState().shadowOffset.y = y;
2002 0 : return NS_OK;
2003 : }
2004 :
2005 : NS_IMETHODIMP
2006 0 : nsCanvasRenderingContext2DAzure::GetShadowOffsetY(float *y)
2007 : {
2008 0 : *y = static_cast<float>(CurrentState().shadowOffset.y);
2009 0 : return NS_OK;
2010 : }
2011 :
2012 : NS_IMETHODIMP
2013 0 : nsCanvasRenderingContext2DAzure::SetShadowBlur(float blur)
2014 : {
2015 0 : if (!FloatValidate(blur) || blur < 0.0) {
2016 0 : return NS_OK;
2017 : }
2018 :
2019 0 : CurrentState().shadowBlur = blur;
2020 0 : return NS_OK;
2021 : }
2022 :
2023 : NS_IMETHODIMP
2024 0 : nsCanvasRenderingContext2DAzure::GetShadowBlur(float *blur)
2025 : {
2026 0 : *blur = CurrentState().shadowBlur;
2027 0 : return NS_OK;
2028 : }
2029 :
2030 : NS_IMETHODIMP
2031 0 : nsCanvasRenderingContext2DAzure::SetShadowColor(const nsAString& colorstr)
2032 : {
2033 : nsIDocument* document = mCanvasElement ?
2034 0 : HTMLCanvasElement()->OwnerDoc() : nsnull;
2035 :
2036 : // Pass the CSS Loader object to the parser, to allow parser error reports
2037 : // to include the outer window ID.
2038 0 : nsCSSParser parser(document ? document->CSSLoader() : nsnull);
2039 : nscolor color;
2040 0 : nsresult rv = parser.ParseColorString(colorstr, nsnull, 0, &color);
2041 0 : if (NS_FAILED(rv)) {
2042 : // Error reporting happens inside the CSS parser
2043 0 : return NS_OK;
2044 : }
2045 0 : CurrentState().shadowColor = color;
2046 :
2047 0 : return NS_OK;
2048 : }
2049 :
2050 : NS_IMETHODIMP
2051 0 : nsCanvasRenderingContext2DAzure::GetShadowColor(nsAString& color)
2052 : {
2053 0 : StyleColorToString(CurrentState().shadowColor, color);
2054 :
2055 0 : return NS_OK;
2056 : }
2057 :
2058 : //
2059 : // rects
2060 : //
2061 :
2062 : NS_IMETHODIMP
2063 0 : nsCanvasRenderingContext2DAzure::ClearRect(float x, float y, float w, float h)
2064 : {
2065 0 : if (!FloatValidate(x,y,w,h)) {
2066 0 : return NS_OK;
2067 : }
2068 :
2069 0 : mTarget->ClearRect(mgfx::Rect(x, y, w, h));
2070 :
2071 0 : return RedrawUser(gfxRect(x, y, w, h));
2072 : }
2073 :
2074 : NS_IMETHODIMP
2075 0 : nsCanvasRenderingContext2DAzure::FillRect(float x, float y, float w, float h)
2076 : {
2077 0 : if (!FloatValidate(x,y,w,h)) {
2078 0 : return NS_OK;
2079 : }
2080 :
2081 0 : const ContextState &state = CurrentState();
2082 :
2083 0 : if (state.patternStyles[STYLE_FILL]) {
2084 : nsCanvasPatternAzure::RepeatMode repeat =
2085 0 : state.patternStyles[STYLE_FILL]->mRepeat;
2086 : // In the FillRect case repeat modes are easy to deal with.
2087 0 : bool limitx = repeat == nsCanvasPatternAzure::NOREPEAT || repeat == nsCanvasPatternAzure::REPEATY;
2088 0 : bool limity = repeat == nsCanvasPatternAzure::NOREPEAT || repeat == nsCanvasPatternAzure::REPEATX;
2089 :
2090 : IntSize patternSize =
2091 0 : state.patternStyles[STYLE_FILL]->mSurface->GetSize();
2092 :
2093 : // We always need to execute painting for non-over operators, even if
2094 : // we end up with w/h = 0.
2095 0 : if (limitx) {
2096 0 : if (x < 0) {
2097 0 : w += x;
2098 0 : if (w < 0) {
2099 0 : w = 0;
2100 : }
2101 :
2102 0 : x = 0;
2103 : }
2104 0 : if (x + w > patternSize.width) {
2105 0 : w = patternSize.width - x;
2106 0 : if (w < 0) {
2107 0 : w = 0;
2108 : }
2109 : }
2110 : }
2111 0 : if (limity) {
2112 0 : if (y < 0) {
2113 0 : h += y;
2114 0 : if (h < 0) {
2115 0 : h = 0;
2116 : }
2117 :
2118 0 : y = 0;
2119 : }
2120 0 : if (y + h > patternSize.height) {
2121 0 : h = patternSize.height - y;
2122 0 : if (h < 0) {
2123 0 : h = 0;
2124 : }
2125 : }
2126 : }
2127 : }
2128 :
2129 0 : mgfx::Rect bounds;
2130 :
2131 0 : if (NeedToDrawShadow()) {
2132 0 : bounds = mgfx::Rect(x, y, w, h);
2133 0 : bounds = mTarget->GetTransform().TransformBounds(bounds);
2134 : }
2135 :
2136 0 : AdjustedTarget(this, bounds.IsEmpty() ? nsnull : &bounds)->
2137 : FillRect(mgfx::Rect(x, y, w, h),
2138 0 : GeneralPattern().ForStyle(this, STYLE_FILL, mTarget),
2139 0 : DrawOptions(state.globalAlpha, UsedOperation()));
2140 :
2141 0 : return RedrawUser(gfxRect(x, y, w, h));
2142 : }
2143 :
2144 : NS_IMETHODIMP
2145 0 : nsCanvasRenderingContext2DAzure::StrokeRect(float x, float y, float w, float h)
2146 : {
2147 0 : if (!FloatValidate(x,y,w,h)) {
2148 0 : return NS_OK;
2149 : }
2150 :
2151 0 : const ContextState &state = CurrentState();
2152 :
2153 0 : mgfx::Rect bounds;
2154 :
2155 0 : if (NeedToDrawShadow()) {
2156 : bounds = mgfx::Rect(x - state.lineWidth / 2.0f, y - state.lineWidth / 2.0f,
2157 0 : w + state.lineWidth, h + state.lineWidth);
2158 0 : bounds = mTarget->GetTransform().TransformBounds(bounds);
2159 : }
2160 :
2161 0 : if (!w && !h) {
2162 0 : return NS_OK;
2163 0 : } else if (!h) {
2164 0 : CapStyle cap = CAP_BUTT;
2165 0 : if (state.lineJoin == JOIN_ROUND) {
2166 0 : cap = CAP_ROUND;
2167 : }
2168 0 : AdjustedTarget(this, bounds.IsEmpty() ? nsnull : &bounds)->
2169 : StrokeLine(Point(x, y), Point(x + w, y),
2170 0 : GeneralPattern().ForStyle(this, STYLE_STROKE, mTarget),
2171 : StrokeOptions(state.lineWidth, state.lineJoin,
2172 : cap, state.miterLimit,
2173 : state.dash.Length(),
2174 : state.dash.Elements(),
2175 : state.dashOffset),
2176 0 : DrawOptions(state.globalAlpha, UsedOperation()));
2177 0 : return NS_OK;
2178 0 : } else if (!w) {
2179 0 : CapStyle cap = CAP_BUTT;
2180 0 : if (state.lineJoin == JOIN_ROUND) {
2181 0 : cap = CAP_ROUND;
2182 : }
2183 0 : AdjustedTarget(this, bounds.IsEmpty() ? nsnull : &bounds)->
2184 : StrokeLine(Point(x, y), Point(x, y + h),
2185 0 : GeneralPattern().ForStyle(this, STYLE_STROKE, mTarget),
2186 : StrokeOptions(state.lineWidth, state.lineJoin,
2187 : cap, state.miterLimit,
2188 : state.dash.Length(),
2189 : state.dash.Elements(),
2190 : state.dashOffset),
2191 0 : DrawOptions(state.globalAlpha, UsedOperation()));
2192 0 : return NS_OK;
2193 : }
2194 :
2195 0 : AdjustedTarget(this, bounds.IsEmpty() ? nsnull : &bounds)->
2196 : StrokeRect(mgfx::Rect(x, y, w, h),
2197 0 : GeneralPattern().ForStyle(this, STYLE_STROKE, mTarget),
2198 : StrokeOptions(state.lineWidth, state.lineJoin,
2199 : state.lineCap, state.miterLimit,
2200 : state.dash.Length(),
2201 : state.dash.Elements(),
2202 : state.dashOffset),
2203 0 : DrawOptions(state.globalAlpha, UsedOperation()));
2204 :
2205 0 : return Redraw();
2206 : }
2207 :
2208 : //
2209 : // path bits
2210 : //
2211 :
2212 : NS_IMETHODIMP
2213 0 : nsCanvasRenderingContext2DAzure::BeginPath()
2214 : {
2215 0 : mPath = nsnull;
2216 0 : mPathBuilder = nsnull;
2217 0 : mDSPathBuilder = nsnull;
2218 0 : mPathTransformWillUpdate = false;
2219 0 : return NS_OK;
2220 : }
2221 :
2222 : NS_IMETHODIMP
2223 0 : nsCanvasRenderingContext2DAzure::ClosePath()
2224 : {
2225 0 : EnsureWritablePath();
2226 :
2227 0 : if (mPathBuilder) {
2228 0 : mPathBuilder->Close();
2229 : } else {
2230 0 : mDSPathBuilder->Close();
2231 : }
2232 :
2233 0 : return NS_OK;
2234 : }
2235 :
2236 : NS_IMETHODIMP
2237 0 : nsCanvasRenderingContext2DAzure::Fill()
2238 : {
2239 0 : EnsureUserSpacePath();
2240 :
2241 0 : if (!mPath) {
2242 0 : return NS_OK;
2243 : }
2244 :
2245 0 : mgfx::Rect bounds;
2246 :
2247 0 : if (NeedToDrawShadow()) {
2248 0 : bounds = mPath->GetBounds(mTarget->GetTransform());
2249 : }
2250 :
2251 0 : AdjustedTarget(this, bounds.IsEmpty() ? nsnull : &bounds)->
2252 0 : Fill(mPath, GeneralPattern().ForStyle(this, STYLE_FILL, mTarget),
2253 0 : DrawOptions(CurrentState().globalAlpha, UsedOperation()));
2254 :
2255 0 : return Redraw();
2256 : }
2257 :
2258 : NS_IMETHODIMP
2259 0 : nsCanvasRenderingContext2DAzure::Stroke()
2260 : {
2261 0 : EnsureUserSpacePath();
2262 :
2263 0 : if (!mPath) {
2264 0 : return NS_OK;
2265 : }
2266 :
2267 0 : const ContextState &state = CurrentState();
2268 :
2269 : StrokeOptions strokeOptions(state.lineWidth, state.lineJoin,
2270 : state.lineCap, state.miterLimit,
2271 : state.dash.Length(), state.dash.Elements(),
2272 0 : state.dashOffset);
2273 :
2274 0 : mgfx::Rect bounds;
2275 0 : if (NeedToDrawShadow()) {
2276 : bounds =
2277 0 : mPath->GetStrokedBounds(strokeOptions, mTarget->GetTransform());
2278 : }
2279 :
2280 0 : AdjustedTarget(this, bounds.IsEmpty() ? nsnull : &bounds)->
2281 0 : Stroke(mPath, GeneralPattern().ForStyle(this, STYLE_STROKE, mTarget),
2282 0 : strokeOptions, DrawOptions(state.globalAlpha, UsedOperation()));
2283 :
2284 0 : return Redraw();
2285 : }
2286 :
2287 : NS_IMETHODIMP
2288 0 : nsCanvasRenderingContext2DAzure::Clip()
2289 : {
2290 0 : EnsureUserSpacePath();
2291 :
2292 0 : if (!mPath) {
2293 0 : return NS_OK;
2294 : }
2295 :
2296 0 : mTarget->PushClip(mPath);
2297 0 : CurrentState().clipsPushed.push_back(mPath);
2298 :
2299 0 : return NS_OK;
2300 : }
2301 :
2302 : NS_IMETHODIMP
2303 0 : nsCanvasRenderingContext2DAzure::MoveTo(float x, float y)
2304 : {
2305 0 : if (!FloatValidate(x,y))
2306 0 : return NS_OK;
2307 :
2308 0 : EnsureWritablePath();
2309 :
2310 0 : if (mPathBuilder) {
2311 0 : mPathBuilder->MoveTo(Point(x, y));
2312 : } else {
2313 0 : mDSPathBuilder->MoveTo(mTarget->GetTransform() * Point(x, y));
2314 : }
2315 :
2316 0 : return NS_OK;
2317 : }
2318 :
2319 : NS_IMETHODIMP
2320 0 : nsCanvasRenderingContext2DAzure::LineTo(float x, float y)
2321 : {
2322 0 : if (!FloatValidate(x,y))
2323 0 : return NS_OK;
2324 :
2325 0 : EnsureWritablePath();
2326 :
2327 0 : return LineTo(Point(x, y));;
2328 : }
2329 :
2330 : nsresult
2331 0 : nsCanvasRenderingContext2DAzure::LineTo(const Point& aPoint)
2332 : {
2333 0 : if (mPathBuilder) {
2334 0 : mPathBuilder->LineTo(aPoint);
2335 : } else {
2336 0 : mDSPathBuilder->LineTo(mTarget->GetTransform() * aPoint);
2337 : }
2338 :
2339 0 : return NS_OK;
2340 : }
2341 :
2342 : NS_IMETHODIMP
2343 0 : nsCanvasRenderingContext2DAzure::QuadraticCurveTo(float cpx, float cpy, float x, float y)
2344 : {
2345 0 : if (!FloatValidate(cpx, cpy, x, y)) {
2346 0 : return NS_OK;
2347 : }
2348 :
2349 0 : EnsureWritablePath();
2350 :
2351 0 : if (mPathBuilder) {
2352 0 : mPathBuilder->QuadraticBezierTo(Point(cpx, cpy), Point(x, y));
2353 : } else {
2354 0 : Matrix transform = mTarget->GetTransform();
2355 0 : mDSPathBuilder->QuadraticBezierTo(transform * Point(cpx, cpy), transform * Point(x, y));
2356 : }
2357 :
2358 0 : return NS_OK;
2359 : }
2360 :
2361 : NS_IMETHODIMP
2362 0 : nsCanvasRenderingContext2DAzure::BezierCurveTo(float cp1x, float cp1y,
2363 : float cp2x, float cp2y,
2364 : float x, float y)
2365 : {
2366 0 : if (!FloatValidate(cp1x, cp1y, cp2x, cp2y, x, y)) {
2367 0 : return NS_OK;
2368 : }
2369 :
2370 0 : EnsureWritablePath();
2371 :
2372 0 : return BezierTo(Point(cp1x, cp1y), Point(cp2x, cp2y), Point(x, y));
2373 : }
2374 :
2375 : nsresult
2376 0 : nsCanvasRenderingContext2DAzure::BezierTo(const Point& aCP1,
2377 : const Point& aCP2,
2378 : const Point& aCP3)
2379 : {
2380 0 : if (mPathBuilder) {
2381 0 : mPathBuilder->BezierTo(aCP1, aCP2, aCP3);
2382 : } else {
2383 0 : Matrix transform = mTarget->GetTransform();
2384 0 : mDSPathBuilder->BezierTo(transform * aCP1,
2385 : transform * aCP2,
2386 0 : transform * aCP3);
2387 : }
2388 :
2389 0 : return NS_OK;
2390 : }
2391 :
2392 : NS_IMETHODIMP
2393 0 : nsCanvasRenderingContext2DAzure::ArcTo(float x1, float y1, float x2, float y2, float radius)
2394 : {
2395 0 : if (!FloatValidate(x1, y1, x2, y2, radius)) {
2396 0 : return NS_OK;
2397 : }
2398 :
2399 0 : if (radius < 0) {
2400 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
2401 : }
2402 :
2403 0 : EnsureWritablePath();
2404 :
2405 : // Current point in user space!
2406 0 : Point p0;
2407 0 : if (mPathBuilder) {
2408 0 : p0 = mPathBuilder->CurrentPoint();
2409 : } else {
2410 0 : Matrix invTransform = mTarget->GetTransform();
2411 0 : if (!invTransform.Invert()) {
2412 0 : return NS_OK;
2413 : }
2414 :
2415 0 : p0 = invTransform * mDSPathBuilder->CurrentPoint();
2416 : }
2417 :
2418 0 : Point p1(x1, y1);
2419 0 : Point p2(x2, y2);
2420 :
2421 : // Execute these calculations in double precision to avoid cumulative
2422 : // rounding errors.
2423 : double dir, a2, b2, c2, cosx, sinx, d, anx, any,
2424 : bnx, bny, x3, y3, x4, y4, cx, cy, angle0, angle1;
2425 : bool anticlockwise;
2426 :
2427 0 : if (p0 == p1 || p1 == p2 || radius == 0) {
2428 0 : LineTo(p1.x, p1.y);
2429 0 : return NS_OK;
2430 : }
2431 :
2432 : // Check for colinearity
2433 0 : dir = (p2.x - p1.x) * (p0.y - p1.y) + (p2.y - p1.y) * (p1.x - p0.x);
2434 0 : if (dir == 0) {
2435 0 : LineTo(p1.x, p1.y);
2436 0 : return NS_OK;
2437 : }
2438 :
2439 :
2440 : // XXX - Math for this code was already available from the non-azure code
2441 : // and would be well tested. Perhaps converting to bezier directly might
2442 : // be more efficient longer run.
2443 0 : a2 = (p0.x-x1)*(p0.x-x1) + (p0.y-y1)*(p0.y-y1);
2444 0 : b2 = (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);
2445 0 : c2 = (p0.x-x2)*(p0.x-x2) + (p0.y-y2)*(p0.y-y2);
2446 0 : cosx = (a2+b2-c2)/(2*sqrt(a2*b2));
2447 :
2448 0 : sinx = sqrt(1 - cosx*cosx);
2449 0 : d = radius / ((1 - cosx) / sinx);
2450 :
2451 0 : anx = (x1-p0.x) / sqrt(a2);
2452 0 : any = (y1-p0.y) / sqrt(a2);
2453 0 : bnx = (x1-x2) / sqrt(b2);
2454 0 : bny = (y1-y2) / sqrt(b2);
2455 0 : x3 = x1 - anx*d;
2456 0 : y3 = y1 - any*d;
2457 0 : x4 = x1 - bnx*d;
2458 0 : y4 = y1 - bny*d;
2459 0 : anticlockwise = (dir < 0);
2460 0 : cx = x3 + any*radius*(anticlockwise ? 1 : -1);
2461 0 : cy = y3 - anx*radius*(anticlockwise ? 1 : -1);
2462 0 : angle0 = atan2((y3-cy), (x3-cx));
2463 0 : angle1 = atan2((y4-cy), (x4-cx));
2464 :
2465 :
2466 0 : LineTo(x3, y3);
2467 :
2468 0 : Arc(cx, cy, radius, angle0, angle1, anticlockwise);
2469 0 : return NS_OK;
2470 : }
2471 :
2472 : NS_IMETHODIMP
2473 0 : nsCanvasRenderingContext2DAzure::Arc(float x, float y,
2474 : float r,
2475 : float startAngle, float endAngle,
2476 : bool ccw)
2477 : {
2478 0 : if (!FloatValidate(x, y, r, startAngle, endAngle)) {
2479 0 : return NS_OK;
2480 : }
2481 :
2482 0 : if (r < 0.0)
2483 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
2484 :
2485 0 : EnsureWritablePath();
2486 :
2487 0 : ArcToBezier(this, Point(x, y), r, startAngle, endAngle, ccw);
2488 0 : return NS_OK;
2489 : }
2490 :
2491 : NS_IMETHODIMP
2492 0 : nsCanvasRenderingContext2DAzure::Rect(float x, float y, float w, float h)
2493 : {
2494 0 : if (!FloatValidate(x, y, w, h)) {
2495 0 : return NS_OK;
2496 : }
2497 :
2498 0 : EnsureWritablePath();
2499 :
2500 0 : if (mPathBuilder) {
2501 0 : mPathBuilder->MoveTo(Point(x, y));
2502 0 : mPathBuilder->LineTo(Point(x + w, y));
2503 0 : mPathBuilder->LineTo(Point(x + w, y + h));
2504 0 : mPathBuilder->LineTo(Point(x, y + h));
2505 0 : mPathBuilder->Close();
2506 : } else {
2507 0 : mDSPathBuilder->MoveTo(mTarget->GetTransform() * Point(x, y));
2508 0 : mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(x + w, y));
2509 0 : mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(x + w, y + h));
2510 0 : mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(x, y + h));
2511 0 : mDSPathBuilder->Close();
2512 : }
2513 :
2514 0 : return NS_OK;
2515 : }
2516 :
2517 : void
2518 0 : nsCanvasRenderingContext2DAzure::EnsureWritablePath()
2519 : {
2520 0 : if (mDSPathBuilder) {
2521 0 : return;
2522 : }
2523 :
2524 0 : FillRule fillRule = CurrentState().fillRule;
2525 :
2526 0 : if (mPathBuilder) {
2527 0 : if (mPathTransformWillUpdate) {
2528 0 : mPath = mPathBuilder->Finish();
2529 : mDSPathBuilder =
2530 0 : mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
2531 0 : mPath = nsnull;
2532 0 : mPathBuilder = nsnull;
2533 : }
2534 0 : return;
2535 : }
2536 :
2537 0 : if (!mPath) {
2538 0 : mPathBuilder = mTarget->CreatePathBuilder(fillRule);
2539 0 : } else if (!mPathTransformWillUpdate) {
2540 0 : mPathBuilder = mPath->CopyToBuilder(fillRule);
2541 : } else {
2542 : mDSPathBuilder =
2543 0 : mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
2544 0 : mPathTransformWillUpdate = false;
2545 : }
2546 : }
2547 :
2548 : void
2549 0 : nsCanvasRenderingContext2DAzure::EnsureUserSpacePath()
2550 : {
2551 0 : FillRule fillRule = CurrentState().fillRule;
2552 :
2553 0 : if (!mPath && !mPathBuilder && !mDSPathBuilder) {
2554 0 : mPathBuilder = mTarget->CreatePathBuilder(fillRule);
2555 : }
2556 :
2557 0 : if (mPathBuilder) {
2558 0 : mPath = mPathBuilder->Finish();
2559 0 : mPathBuilder = nsnull;
2560 : }
2561 :
2562 0 : if (mPath && mPathTransformWillUpdate) {
2563 : mDSPathBuilder =
2564 0 : mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
2565 0 : mPath = nsnull;
2566 0 : mPathTransformWillUpdate = false;
2567 : }
2568 :
2569 0 : if (mDSPathBuilder) {
2570 0 : RefPtr<Path> dsPath;
2571 0 : dsPath = mDSPathBuilder->Finish();
2572 0 : mDSPathBuilder = nsnull;
2573 :
2574 0 : Matrix inverse = mTarget->GetTransform();
2575 0 : if (!inverse.Invert()) {
2576 : return;
2577 : }
2578 :
2579 : mPathBuilder =
2580 0 : dsPath->TransformedCopyToBuilder(inverse, fillRule);
2581 0 : mPath = mPathBuilder->Finish();
2582 0 : mPathBuilder = nsnull;
2583 : }
2584 :
2585 0 : if (mPath && mPath->GetFillRule() != fillRule) {
2586 0 : mPathBuilder = mPath->CopyToBuilder(fillRule);
2587 0 : mPath = mPathBuilder->Finish();
2588 : }
2589 : }
2590 :
2591 : void
2592 0 : nsCanvasRenderingContext2DAzure::TransformWillUpdate()
2593 : {
2594 : // Store the matrix that would transform the current path to device
2595 : // space.
2596 0 : if (mPath || mPathBuilder) {
2597 0 : if (!mPathTransformWillUpdate) {
2598 : // If the transform has already been updated, but a device space builder
2599 : // has not been created yet mPathToDS contains the right transform to
2600 : // transform the current mPath into device space.
2601 : // We should leave it alone.
2602 0 : mPathToDS = mTarget->GetTransform();
2603 : }
2604 0 : mPathTransformWillUpdate = true;
2605 : }
2606 0 : }
2607 :
2608 : //
2609 : // text
2610 : //
2611 :
2612 : /**
2613 : * Helper function for SetFont that creates a style rule for the given font.
2614 : * @param aFont The CSS font string
2615 : * @param aNode The canvas element
2616 : * @param aResult Pointer in which to place the new style rule.
2617 : * @remark Assumes all pointer arguments are non-null.
2618 : */
2619 : static nsresult
2620 0 : CreateFontStyleRule(const nsAString& aFont,
2621 : nsINode* aNode,
2622 : StyleRule** aResult)
2623 : {
2624 0 : nsRefPtr<StyleRule> rule;
2625 : bool changed;
2626 :
2627 0 : nsIPrincipal* principal = aNode->NodePrincipal();
2628 0 : nsIDocument* document = aNode->OwnerDoc();
2629 :
2630 0 : nsIURI* docURL = document->GetDocumentURI();
2631 0 : nsIURI* baseURL = document->GetDocBaseURI();
2632 :
2633 : // Pass the CSS Loader object to the parser, to allow parser error reports
2634 : // to include the outer window ID.
2635 0 : nsCSSParser parser(document->CSSLoader());
2636 :
2637 0 : nsresult rv = parser.ParseStyleAttribute(EmptyString(), docURL, baseURL,
2638 0 : principal, getter_AddRefs(rule));
2639 0 : if (NS_FAILED(rv)) {
2640 0 : return rv;
2641 : }
2642 :
2643 : rv = parser.ParseProperty(eCSSProperty_font, aFont, docURL, baseURL,
2644 : principal, rule->GetDeclaration(), &changed,
2645 0 : false);
2646 0 : if (NS_FAILED(rv))
2647 0 : return rv;
2648 :
2649 : rv = parser.ParseProperty(eCSSProperty_line_height,
2650 0 : NS_LITERAL_STRING("normal"), docURL, baseURL,
2651 : principal, rule->GetDeclaration(), &changed,
2652 0 : false);
2653 0 : if (NS_FAILED(rv)) {
2654 0 : return rv;
2655 : }
2656 :
2657 0 : rule->RuleMatched();
2658 :
2659 0 : rule.forget(aResult);
2660 0 : return NS_OK;
2661 : }
2662 :
2663 : NS_IMETHODIMP
2664 0 : nsCanvasRenderingContext2DAzure::SetFont(const nsAString& font)
2665 : {
2666 : nsresult rv;
2667 :
2668 : /*
2669 : * If font is defined with relative units (e.g. ems) and the parent
2670 : * style context changes in between calls, setting the font to the
2671 : * same value as previous could result in a different computed value,
2672 : * so we cannot have the optimization where we check if the new font
2673 : * string is equal to the old one.
2674 : */
2675 :
2676 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mCanvasElement);
2677 0 : if (!content && !mDocShell) {
2678 0 : NS_WARNING("Canvas element must be an nsIContent and non-null or a docshell must be provided");
2679 0 : return NS_ERROR_FAILURE;
2680 : }
2681 :
2682 0 : nsIPresShell* presShell = GetPresShell();
2683 0 : if (!presShell) {
2684 0 : return NS_ERROR_FAILURE;
2685 : }
2686 0 : nsIDocument* document = presShell->GetDocument();
2687 :
2688 0 : nsCOMArray<nsIStyleRule> rules;
2689 :
2690 0 : nsRefPtr<css::StyleRule> rule;
2691 0 : rv = CreateFontStyleRule(font, document, getter_AddRefs(rule));
2692 :
2693 0 : if (NS_FAILED(rv)) {
2694 0 : return rv;
2695 : }
2696 :
2697 0 : css::Declaration *declaration = rule->GetDeclaration();
2698 : // The easiest way to see whether we got a syntax error or whether
2699 : // we got 'inherit' or 'initial' is to look at font-size-adjust,
2700 : // which the shorthand resets to either 'none' or
2701 : // '-moz-system-font'.
2702 : // We know the declaration is not !important, so we can use
2703 : // GetNormalBlock().
2704 : const nsCSSValue *fsaVal =
2705 0 : declaration->GetNormalBlock()->ValueFor(eCSSProperty_font_size_adjust);
2706 0 : if (!fsaVal || (fsaVal->GetUnit() != eCSSUnit_None &&
2707 0 : fsaVal->GetUnit() != eCSSUnit_System_Font)) {
2708 : // We got an all-property value or a syntax error. The spec says
2709 : // this value must be ignored.
2710 0 : return NS_OK;
2711 : }
2712 :
2713 0 : rules.AppendObject(rule);
2714 :
2715 0 : nsStyleSet* styleSet = presShell->StyleSet();
2716 :
2717 : // have to get a parent style context for inherit-like relative
2718 : // values (2em, bolder, etc.)
2719 0 : nsRefPtr<nsStyleContext> parentContext;
2720 :
2721 0 : if (content && content->IsInDoc()) {
2722 : // inherit from the canvas element
2723 : parentContext = nsComputedDOMStyle::GetStyleContextForElement(
2724 0 : content->AsElement(),
2725 : nsnull,
2726 0 : presShell);
2727 : } else {
2728 : // otherwise inherit from default (10px sans-serif)
2729 0 : nsRefPtr<css::StyleRule> parentRule;
2730 0 : rv = CreateFontStyleRule(NS_LITERAL_STRING("10px sans-serif"),
2731 : document,
2732 0 : getter_AddRefs(parentRule));
2733 :
2734 0 : if (NS_FAILED(rv)) {
2735 0 : return rv;
2736 : }
2737 :
2738 0 : nsCOMArray<nsIStyleRule> parentRules;
2739 0 : parentRules.AppendObject(parentRule);
2740 0 : parentContext = styleSet->ResolveStyleForRules(nsnull, parentRules);
2741 : }
2742 :
2743 0 : if (!parentContext) {
2744 0 : return NS_ERROR_FAILURE;
2745 : }
2746 :
2747 : nsRefPtr<nsStyleContext> sc =
2748 0 : styleSet->ResolveStyleForRules(parentContext, rules);
2749 0 : if (!sc) {
2750 0 : return NS_ERROR_FAILURE;
2751 : }
2752 :
2753 0 : const nsStyleFont* fontStyle = sc->GetStyleFont();
2754 :
2755 0 : NS_ASSERTION(fontStyle, "Could not obtain font style");
2756 :
2757 0 : nsIAtom* language = sc->GetStyleFont()->mLanguage;
2758 0 : if (!language) {
2759 0 : language = presShell->GetPresContext()->GetLanguageFromCharset();
2760 : }
2761 :
2762 : // use CSS pixels instead of dev pixels to avoid being affected by page zoom
2763 0 : const PRUint32 aupcp = nsPresContext::AppUnitsPerCSSPixel();
2764 : // un-zoom the font size to avoid being affected by text-only zoom
2765 : //
2766 : // Purposely ignore the font size that respects the user's minimum
2767 : // font preference (fontStyle->mFont.size) in favor of the computed
2768 : // size (fontStyle->mSize). See
2769 : // https://bugzilla.mozilla.org/show_bug.cgi?id=698652.
2770 0 : const nscoord fontSize = nsStyleFont::UnZoomText(parentContext->PresContext(), fontStyle->mSize);
2771 :
2772 0 : bool printerFont = (presShell->GetPresContext()->Type() == nsPresContext::eContext_PrintPreview ||
2773 0 : presShell->GetPresContext()->Type() == nsPresContext::eContext_Print);
2774 :
2775 : gfxFontStyle style(fontStyle->mFont.style,
2776 : fontStyle->mFont.weight,
2777 : fontStyle->mFont.stretch,
2778 0 : NSAppUnitsToFloatPixels(fontSize, float(aupcp)),
2779 : language,
2780 : fontStyle->mFont.sizeAdjust,
2781 : fontStyle->mFont.systemFont,
2782 : printerFont,
2783 : fontStyle->mFont.featureSettings,
2784 0 : fontStyle->mFont.languageOverride);
2785 :
2786 0 : CurrentState().fontGroup =
2787 0 : gfxPlatform::GetPlatform()->CreateFontGroup(fontStyle->mFont.name,
2788 : &style,
2789 0 : presShell->GetPresContext()->GetUserFontSet());
2790 0 : NS_ASSERTION(CurrentState().fontGroup, "Could not get font group");
2791 :
2792 : // The font getter is required to be reserialized based on what we
2793 : // parsed (including having line-height removed). (Older drafts of
2794 : // the spec required font sizes be converted to pixels, but that no
2795 : // longer seems to be required.)
2796 0 : declaration->GetValue(eCSSProperty_font, CurrentState().font);
2797 :
2798 0 : return NS_OK;
2799 : }
2800 :
2801 : NS_IMETHODIMP
2802 0 : nsCanvasRenderingContext2DAzure::GetFont(nsAString& font)
2803 : {
2804 : /* will initilize the value if not set, else does nothing */
2805 0 : GetCurrentFontStyle();
2806 :
2807 0 : font = CurrentState().font;
2808 0 : return NS_OK;
2809 : }
2810 :
2811 : NS_IMETHODIMP
2812 0 : nsCanvasRenderingContext2DAzure::SetTextAlign(const nsAString& ta)
2813 : {
2814 0 : if (ta.EqualsLiteral("start"))
2815 0 : CurrentState().textAlign = TEXT_ALIGN_START;
2816 0 : else if (ta.EqualsLiteral("end"))
2817 0 : CurrentState().textAlign = TEXT_ALIGN_END;
2818 0 : else if (ta.EqualsLiteral("left"))
2819 0 : CurrentState().textAlign = TEXT_ALIGN_LEFT;
2820 0 : else if (ta.EqualsLiteral("right"))
2821 0 : CurrentState().textAlign = TEXT_ALIGN_RIGHT;
2822 0 : else if (ta.EqualsLiteral("center"))
2823 0 : CurrentState().textAlign = TEXT_ALIGN_CENTER;
2824 :
2825 0 : return NS_OK;
2826 : }
2827 :
2828 : NS_IMETHODIMP
2829 0 : nsCanvasRenderingContext2DAzure::GetTextAlign(nsAString& ta)
2830 : {
2831 0 : switch (CurrentState().textAlign)
2832 : {
2833 : case TEXT_ALIGN_START:
2834 0 : ta.AssignLiteral("start");
2835 0 : break;
2836 : case TEXT_ALIGN_END:
2837 0 : ta.AssignLiteral("end");
2838 0 : break;
2839 : case TEXT_ALIGN_LEFT:
2840 0 : ta.AssignLiteral("left");
2841 0 : break;
2842 : case TEXT_ALIGN_RIGHT:
2843 0 : ta.AssignLiteral("right");
2844 0 : break;
2845 : case TEXT_ALIGN_CENTER:
2846 0 : ta.AssignLiteral("center");
2847 0 : break;
2848 : }
2849 :
2850 0 : return NS_OK;
2851 : }
2852 :
2853 : NS_IMETHODIMP
2854 0 : nsCanvasRenderingContext2DAzure::SetTextBaseline(const nsAString& tb)
2855 : {
2856 0 : if (tb.EqualsLiteral("top"))
2857 0 : CurrentState().textBaseline = TEXT_BASELINE_TOP;
2858 0 : else if (tb.EqualsLiteral("hanging"))
2859 0 : CurrentState().textBaseline = TEXT_BASELINE_HANGING;
2860 0 : else if (tb.EqualsLiteral("middle"))
2861 0 : CurrentState().textBaseline = TEXT_BASELINE_MIDDLE;
2862 0 : else if (tb.EqualsLiteral("alphabetic"))
2863 0 : CurrentState().textBaseline = TEXT_BASELINE_ALPHABETIC;
2864 0 : else if (tb.EqualsLiteral("ideographic"))
2865 0 : CurrentState().textBaseline = TEXT_BASELINE_IDEOGRAPHIC;
2866 0 : else if (tb.EqualsLiteral("bottom"))
2867 0 : CurrentState().textBaseline = TEXT_BASELINE_BOTTOM;
2868 :
2869 0 : return NS_OK;
2870 : }
2871 :
2872 : NS_IMETHODIMP
2873 0 : nsCanvasRenderingContext2DAzure::GetTextBaseline(nsAString& tb)
2874 : {
2875 0 : switch (CurrentState().textBaseline)
2876 : {
2877 : case TEXT_BASELINE_TOP:
2878 0 : tb.AssignLiteral("top");
2879 0 : break;
2880 : case TEXT_BASELINE_HANGING:
2881 0 : tb.AssignLiteral("hanging");
2882 0 : break;
2883 : case TEXT_BASELINE_MIDDLE:
2884 0 : tb.AssignLiteral("middle");
2885 0 : break;
2886 : case TEXT_BASELINE_ALPHABETIC:
2887 0 : tb.AssignLiteral("alphabetic");
2888 0 : break;
2889 : case TEXT_BASELINE_IDEOGRAPHIC:
2890 0 : tb.AssignLiteral("ideographic");
2891 0 : break;
2892 : case TEXT_BASELINE_BOTTOM:
2893 0 : tb.AssignLiteral("bottom");
2894 0 : break;
2895 : }
2896 :
2897 0 : return NS_OK;
2898 : }
2899 :
2900 : /*
2901 : * Helper function that replaces the whitespace characters in a string
2902 : * with U+0020 SPACE. The whitespace characters are defined as U+0020 SPACE,
2903 : * U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF), U+000B LINE
2904 : * TABULATION, U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR).
2905 : * @param str The string whose whitespace characters to replace.
2906 : */
2907 : static inline void
2908 0 : TextReplaceWhitespaceCharacters(nsAutoString& str)
2909 : {
2910 0 : str.ReplaceChar("\x09\x0A\x0B\x0C\x0D", PRUnichar(' '));
2911 0 : }
2912 :
2913 : NS_IMETHODIMP
2914 0 : nsCanvasRenderingContext2DAzure::FillText(const nsAString& text, float x, float y, float maxWidth)
2915 : {
2916 0 : return DrawOrMeasureText(text, x, y, maxWidth, TEXT_DRAW_OPERATION_FILL, nsnull);
2917 : }
2918 :
2919 : NS_IMETHODIMP
2920 0 : nsCanvasRenderingContext2DAzure::StrokeText(const nsAString& text, float x, float y, float maxWidth)
2921 : {
2922 0 : return DrawOrMeasureText(text, x, y, maxWidth, TEXT_DRAW_OPERATION_STROKE, nsnull);
2923 : }
2924 :
2925 : NS_IMETHODIMP
2926 0 : nsCanvasRenderingContext2DAzure::MeasureText(const nsAString& rawText,
2927 : nsIDOMTextMetrics** _retval)
2928 : {
2929 : float width;
2930 :
2931 0 : nsresult rv = DrawOrMeasureText(rawText, 0, 0, 0, TEXT_DRAW_OPERATION_MEASURE, &width);
2932 :
2933 0 : if (NS_FAILED(rv)) {
2934 0 : return rv;
2935 : }
2936 :
2937 0 : nsRefPtr<nsIDOMTextMetrics> textMetrics = new nsTextMetricsAzure(width);
2938 0 : if (!textMetrics.get()) {
2939 0 : return NS_ERROR_OUT_OF_MEMORY;
2940 : }
2941 :
2942 0 : *_retval = textMetrics.forget().get();
2943 :
2944 0 : return NS_OK;
2945 : }
2946 :
2947 : /**
2948 : * Used for nsBidiPresUtils::ProcessText
2949 : */
2950 : struct NS_STACK_CLASS nsCanvasBidiProcessorAzure : public nsBidiPresUtils::BidiProcessor
2951 0 : {
2952 : typedef nsCanvasRenderingContext2DAzure::ContextState ContextState;
2953 :
2954 0 : virtual void SetText(const PRUnichar* text, PRInt32 length, nsBidiDirection direction)
2955 : {
2956 0 : mFontgrp->UpdateFontList(); // ensure user font generation is current
2957 : mTextRun = mFontgrp->MakeTextRun(text,
2958 : length,
2959 : mThebes,
2960 : mAppUnitsPerDevPixel,
2961 0 : direction==NSBIDI_RTL ? gfxTextRunFactory::TEXT_IS_RTL : 0);
2962 0 : }
2963 :
2964 0 : virtual nscoord GetWidth()
2965 : {
2966 : gfxTextRun::Metrics textRunMetrics = mTextRun->MeasureText(0,
2967 : mTextRun->GetLength(),
2968 : mDoMeasureBoundingBox ?
2969 : gfxFont::TIGHT_INK_EXTENTS :
2970 : gfxFont::LOOSE_INK_EXTENTS,
2971 : mThebes,
2972 0 : nsnull);
2973 :
2974 : // this only measures the height; the total width is gotten from the
2975 : // the return value of ProcessText.
2976 0 : if (mDoMeasureBoundingBox) {
2977 0 : textRunMetrics.mBoundingBox.Scale(1.0 / mAppUnitsPerDevPixel);
2978 0 : mBoundingBox = mBoundingBox.Union(textRunMetrics.mBoundingBox);
2979 : }
2980 :
2981 0 : return NSToCoordRound(textRunMetrics.mAdvanceWidth);
2982 : }
2983 :
2984 0 : virtual void DrawText(nscoord xOffset, nscoord width)
2985 : {
2986 0 : gfxPoint point = mPt;
2987 0 : point.x += xOffset;
2988 :
2989 : // offset is given in terms of left side of string
2990 0 : if (mTextRun->IsRightToLeft()) {
2991 : // Bug 581092 - don't use rounded pixel width to advance to
2992 : // right-hand end of run, because this will cause different
2993 : // glyph positioning for LTR vs RTL drawing of the same
2994 : // glyph string on OS X and DWrite where textrun widths may
2995 : // involve fractional pixels.
2996 : gfxTextRun::Metrics textRunMetrics =
2997 : mTextRun->MeasureText(0,
2998 : mTextRun->GetLength(),
2999 : mDoMeasureBoundingBox ?
3000 : gfxFont::TIGHT_INK_EXTENTS :
3001 : gfxFont::LOOSE_INK_EXTENTS,
3002 : mThebes,
3003 0 : nsnull);
3004 0 : point.x += textRunMetrics.mAdvanceWidth;
3005 : // old code was:
3006 : // point.x += width * mAppUnitsPerDevPixel;
3007 : // TODO: restore this if/when we move to fractional coords
3008 : // throughout the text layout process
3009 : }
3010 :
3011 : PRUint32 numRuns;
3012 0 : const gfxTextRun::GlyphRun *runs = mTextRun->GetGlyphRuns(&numRuns);
3013 0 : const PRUint32 appUnitsPerDevUnit = mAppUnitsPerDevPixel;
3014 0 : const double devUnitsPerAppUnit = 1.0/double(appUnitsPerDevUnit);
3015 : Point baselineOrigin =
3016 0 : Point(point.x * devUnitsPerAppUnit, point.y * devUnitsPerAppUnit);
3017 :
3018 0 : float advanceSum = 0;
3019 :
3020 0 : for (PRUint32 c = 0; c < numRuns; c++) {
3021 0 : gfxFont *font = runs[c].mFont;
3022 0 : PRUint32 endRun = 0;
3023 0 : if (c + 1 < numRuns) {
3024 0 : endRun = runs[c + 1].mCharacterOffset;
3025 : } else {
3026 0 : endRun = mTextRun->GetLength();
3027 : }
3028 :
3029 0 : const gfxTextRun::CompressedGlyph *glyphs = mTextRun->GetCharacterGlyphs();
3030 :
3031 : RefPtr<ScaledFont> scaledFont =
3032 0 : gfxPlatform::GetPlatform()->GetScaledFontForFont(font);
3033 :
3034 0 : if (!scaledFont) {
3035 : // This can occur when something switched DirectWrite off.
3036 : return;
3037 : }
3038 :
3039 : GlyphBuffer buffer;
3040 :
3041 0 : std::vector<Glyph> glyphBuf;
3042 :
3043 0 : for (PRUint32 i = runs[c].mCharacterOffset; i < endRun; i++) {
3044 0 : Glyph newGlyph;
3045 0 : if (glyphs[i].IsSimpleGlyph()) {
3046 0 : newGlyph.mIndex = glyphs[i].GetSimpleGlyph();
3047 0 : if (mTextRun->IsRightToLeft()) {
3048 : newGlyph.mPosition.x = baselineOrigin.x - advanceSum -
3049 0 : glyphs[i].GetSimpleAdvance() * devUnitsPerAppUnit;
3050 : } else {
3051 0 : newGlyph.mPosition.x = baselineOrigin.x + advanceSum;
3052 : }
3053 0 : newGlyph.mPosition.y = baselineOrigin.y;
3054 0 : advanceSum += glyphs[i].GetSimpleAdvance() * devUnitsPerAppUnit;
3055 0 : glyphBuf.push_back(newGlyph);
3056 0 : continue;
3057 : }
3058 :
3059 0 : if (!glyphs[i].GetGlyphCount()) {
3060 0 : continue;
3061 : }
3062 :
3063 : gfxTextRun::DetailedGlyph *detailedGlyphs =
3064 0 : mTextRun->GetDetailedGlyphs(i);
3065 :
3066 0 : for (PRUint32 c = 0; c < glyphs[i].GetGlyphCount(); c++) {
3067 0 : newGlyph.mIndex = detailedGlyphs[c].mGlyphID;
3068 0 : if (mTextRun->IsRightToLeft()) {
3069 0 : newGlyph.mPosition.x = baselineOrigin.x + detailedGlyphs[c].mXOffset * devUnitsPerAppUnit -
3070 0 : advanceSum - detailedGlyphs[c].mAdvance * devUnitsPerAppUnit;
3071 : } else {
3072 0 : newGlyph.mPosition.x = baselineOrigin.x + detailedGlyphs[c].mXOffset * devUnitsPerAppUnit + advanceSum;
3073 : }
3074 0 : newGlyph.mPosition.y = baselineOrigin.y + detailedGlyphs[c].mYOffset * devUnitsPerAppUnit;
3075 0 : glyphBuf.push_back(newGlyph);
3076 0 : advanceSum += detailedGlyphs[c].mAdvance * devUnitsPerAppUnit;
3077 : }
3078 : }
3079 :
3080 0 : if (!glyphBuf.size()) {
3081 : // This may happen for glyph runs for a 0 size font.
3082 0 : continue;
3083 : }
3084 :
3085 0 : buffer.mGlyphs = &glyphBuf.front();
3086 0 : buffer.mNumGlyphs = glyphBuf.size();
3087 :
3088 0 : if (mOp == nsCanvasRenderingContext2DAzure::TEXT_DRAW_OPERATION_FILL) {
3089 0 : nsCanvasRenderingContext2DAzure::AdjustedTarget(mCtx)->
3090 : FillGlyphs(scaledFont, buffer,
3091 0 : nsCanvasRenderingContext2DAzure::GeneralPattern().
3092 0 : ForStyle(mCtx, nsCanvasRenderingContext2DAzure::STYLE_FILL, mCtx->mTarget),
3093 0 : DrawOptions(mState->globalAlpha, mCtx->UsedOperation()));
3094 0 : } else if (mOp == nsCanvasRenderingContext2DAzure::TEXT_DRAW_OPERATION_STROKE) {
3095 0 : RefPtr<Path> path = scaledFont->GetPathForGlyphs(buffer, mCtx->mTarget);
3096 :
3097 0 : const ContextState& state = *mState;
3098 0 : nsCanvasRenderingContext2DAzure::AdjustedTarget(mCtx)->
3099 0 : Stroke(path, nsCanvasRenderingContext2DAzure::GeneralPattern().
3100 0 : ForStyle(mCtx, nsCanvasRenderingContext2DAzure::STYLE_STROKE, mCtx->mTarget),
3101 : StrokeOptions(state.lineWidth, state.lineJoin,
3102 : state.lineCap, state.miterLimit,
3103 : state.dash.Length(),
3104 : state.dash.Elements(),
3105 : state.dashOffset),
3106 0 : DrawOptions(state.globalAlpha, mCtx->UsedOperation()));
3107 :
3108 : }
3109 : }
3110 : }
3111 :
3112 : // current text run
3113 : nsAutoPtr<gfxTextRun> mTextRun;
3114 :
3115 : // pointer to a screen reference context used to measure text and such
3116 : nsRefPtr<gfxContext> mThebes;
3117 :
3118 : // Pointer to the draw target we should fill our text to
3119 : nsCanvasRenderingContext2DAzure *mCtx;
3120 :
3121 : // position of the left side of the string, alphabetic baseline
3122 : gfxPoint mPt;
3123 :
3124 : // current font
3125 : gfxFontGroup* mFontgrp;
3126 :
3127 : // dev pixel conversion factor
3128 : PRUint32 mAppUnitsPerDevPixel;
3129 :
3130 : // operation (fill or stroke)
3131 : nsCanvasRenderingContext2DAzure::TextDrawOperation mOp;
3132 :
3133 : // context state
3134 : ContextState *mState;
3135 :
3136 : // union of bounding boxes of all runs, needed for shadows
3137 : gfxRect mBoundingBox;
3138 :
3139 : // true iff the bounding box should be measured
3140 : bool mDoMeasureBoundingBox;
3141 : };
3142 :
3143 : nsresult
3144 0 : nsCanvasRenderingContext2DAzure::DrawOrMeasureText(const nsAString& aRawText,
3145 : float aX,
3146 : float aY,
3147 : float aMaxWidth,
3148 : TextDrawOperation aOp,
3149 : float* aWidth)
3150 : {
3151 : nsresult rv;
3152 :
3153 0 : if (!FloatValidate(aX, aY, aMaxWidth))
3154 0 : return NS_ERROR_DOM_SYNTAX_ERR;
3155 :
3156 : // spec isn't clear on what should happen if aMaxWidth <= 0, so
3157 : // treat it as an invalid argument
3158 : // technically, 0 should be an invalid value as well, but 0 is the default
3159 : // arg, and there is no way to tell if the default was used
3160 0 : if (aMaxWidth < 0)
3161 0 : return NS_ERROR_INVALID_ARG;
3162 :
3163 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mCanvasElement);
3164 0 : if (!content && !mDocShell) {
3165 0 : NS_WARNING("Canvas element must be an nsIContent and non-null or a docshell must be provided");
3166 0 : return NS_ERROR_FAILURE;
3167 : }
3168 :
3169 0 : nsIPresShell* presShell = GetPresShell();
3170 0 : if (!presShell)
3171 0 : return NS_ERROR_FAILURE;
3172 :
3173 0 : nsIDocument* document = presShell->GetDocument();
3174 :
3175 : // replace all the whitespace characters with U+0020 SPACE
3176 0 : nsAutoString textToDraw(aRawText);
3177 0 : TextReplaceWhitespaceCharacters(textToDraw);
3178 :
3179 : // for now, default to ltr if not in doc
3180 0 : bool isRTL = false;
3181 :
3182 0 : if (content && content->IsInDoc()) {
3183 : // try to find the closest context
3184 : nsRefPtr<nsStyleContext> canvasStyle =
3185 0 : nsComputedDOMStyle::GetStyleContextForElement(content->AsElement(),
3186 : nsnull,
3187 0 : presShell);
3188 0 : if (!canvasStyle) {
3189 0 : return NS_ERROR_FAILURE;
3190 : }
3191 :
3192 0 : isRTL = canvasStyle->GetStyleVisibility()->mDirection ==
3193 0 : NS_STYLE_DIRECTION_RTL;
3194 : } else {
3195 0 : isRTL = GET_BIDI_OPTION_DIRECTION(document->GetBidiOptions()) == IBMBIDI_TEXTDIRECTION_RTL;
3196 : }
3197 :
3198 0 : const ContextState &state = CurrentState();
3199 :
3200 : // This is only needed to know if we can know the drawing bounding box easily.
3201 0 : bool doDrawShadow = aOp == TEXT_DRAW_OPERATION_FILL && NeedToDrawShadow();
3202 :
3203 0 : nsCanvasBidiProcessorAzure processor;
3204 :
3205 0 : GetAppUnitsValues(&processor.mAppUnitsPerDevPixel, nsnull);
3206 0 : processor.mPt = gfxPoint(aX, aY);
3207 : processor.mThebes =
3208 0 : new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface());
3209 0 : Matrix matrix = mTarget->GetTransform();
3210 0 : processor.mThebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21, matrix._22, matrix._31, matrix._32));
3211 0 : processor.mCtx = this;
3212 0 : processor.mOp = aOp;
3213 0 : processor.mBoundingBox = gfxRect(0, 0, 0, 0);
3214 0 : processor.mDoMeasureBoundingBox = doDrawShadow || !mIsEntireFrameInvalid;
3215 0 : processor.mState = &CurrentState();
3216 :
3217 :
3218 0 : processor.mFontgrp = GetCurrentFontStyle();
3219 0 : NS_ASSERTION(processor.mFontgrp, "font group is null");
3220 :
3221 : nscoord totalWidthCoord;
3222 :
3223 : // calls bidi algo twice since it needs the full text width and the
3224 : // bounding boxes before rendering anything
3225 0 : nsBidi bidiEngine;
3226 : rv = nsBidiPresUtils::ProcessText(textToDraw.get(),
3227 0 : textToDraw.Length(),
3228 : isRTL ? NSBIDI_RTL : NSBIDI_LTR,
3229 : presShell->GetPresContext(),
3230 : processor,
3231 : nsBidiPresUtils::MODE_MEASURE,
3232 : nsnull,
3233 : 0,
3234 : &totalWidthCoord,
3235 0 : &bidiEngine);
3236 0 : if (NS_FAILED(rv)) {
3237 0 : return rv;
3238 : }
3239 :
3240 0 : float totalWidth = float(totalWidthCoord) / processor.mAppUnitsPerDevPixel;
3241 0 : if (aWidth) {
3242 0 : *aWidth = totalWidth;
3243 : }
3244 :
3245 : // if only measuring, don't need to do any more work
3246 0 : if (aOp==TEXT_DRAW_OPERATION_MEASURE) {
3247 0 : return NS_OK;
3248 : }
3249 :
3250 : // offset pt.x based on text align
3251 : gfxFloat anchorX;
3252 :
3253 0 : if (state.textAlign == TEXT_ALIGN_CENTER) {
3254 0 : anchorX = .5;
3255 0 : } else if (state.textAlign == TEXT_ALIGN_LEFT ||
3256 0 : (!isRTL && state.textAlign == TEXT_ALIGN_START) ||
3257 : (isRTL && state.textAlign == TEXT_ALIGN_END)) {
3258 0 : anchorX = 0;
3259 : } else {
3260 0 : anchorX = 1;
3261 : }
3262 :
3263 0 : processor.mPt.x -= anchorX * totalWidth;
3264 :
3265 : // offset pt.y based on text baseline
3266 0 : NS_ASSERTION(processor.mFontgrp->FontListLength()>0, "font group contains no fonts");
3267 0 : const gfxFont::Metrics& fontMetrics = processor.mFontgrp->GetFontAt(0)->GetMetrics();
3268 :
3269 : gfxFloat anchorY;
3270 :
3271 0 : switch (state.textBaseline)
3272 : {
3273 : case TEXT_BASELINE_HANGING:
3274 : // fall through; best we can do with the information available
3275 : case TEXT_BASELINE_TOP:
3276 0 : anchorY = fontMetrics.emAscent;
3277 0 : break;
3278 : break;
3279 : case TEXT_BASELINE_MIDDLE:
3280 0 : anchorY = (fontMetrics.emAscent - fontMetrics.emDescent) * .5f;
3281 0 : break;
3282 : case TEXT_BASELINE_IDEOGRAPHIC:
3283 : // fall through; best we can do with the information available
3284 : case TEXT_BASELINE_ALPHABETIC:
3285 0 : anchorY = 0;
3286 0 : break;
3287 : case TEXT_BASELINE_BOTTOM:
3288 0 : anchorY = -fontMetrics.emDescent;
3289 0 : break;
3290 : }
3291 :
3292 0 : processor.mPt.y += anchorY;
3293 :
3294 : // correct bounding box to get it to be the correct size/position
3295 0 : processor.mBoundingBox.width = totalWidth;
3296 0 : processor.mBoundingBox.MoveBy(processor.mPt);
3297 :
3298 0 : processor.mPt.x *= processor.mAppUnitsPerDevPixel;
3299 0 : processor.mPt.y *= processor.mAppUnitsPerDevPixel;
3300 :
3301 0 : Matrix oldTransform = mTarget->GetTransform();
3302 : // if text is over aMaxWidth, then scale the text horizontally such that its
3303 : // width is precisely aMaxWidth
3304 0 : if (aMaxWidth > 0 && totalWidth > aMaxWidth) {
3305 0 : Matrix newTransform = oldTransform;
3306 :
3307 : // Translate so that the anchor point is at 0,0, then scale and then
3308 : // translate back.
3309 0 : newTransform.Translate(aX, 0);
3310 0 : newTransform.Scale(aMaxWidth / totalWidth, 1);
3311 0 : newTransform.Translate(-aX, 0);
3312 : /* we do this to avoid an ICE in the android compiler */
3313 0 : Matrix androidCompilerBug = newTransform;
3314 0 : mTarget->SetTransform(androidCompilerBug);
3315 : }
3316 :
3317 : // save the previous bounding box
3318 0 : gfxRect boundingBox = processor.mBoundingBox;
3319 :
3320 : // don't ever need to measure the bounding box twice
3321 0 : processor.mDoMeasureBoundingBox = false;
3322 :
3323 : rv = nsBidiPresUtils::ProcessText(textToDraw.get(),
3324 0 : textToDraw.Length(),
3325 : isRTL ? NSBIDI_RTL : NSBIDI_LTR,
3326 : presShell->GetPresContext(),
3327 : processor,
3328 : nsBidiPresUtils::MODE_DRAW,
3329 : nsnull,
3330 : 0,
3331 : nsnull,
3332 0 : &bidiEngine);
3333 :
3334 :
3335 0 : mTarget->SetTransform(oldTransform);
3336 :
3337 0 : if (aOp == nsCanvasRenderingContext2DAzure::TEXT_DRAW_OPERATION_FILL && !doDrawShadow)
3338 0 : return RedrawUser(boundingBox);
3339 :
3340 0 : return Redraw();
3341 : }
3342 :
3343 : NS_IMETHODIMP
3344 0 : nsCanvasRenderingContext2DAzure::SetMozTextStyle(const nsAString& textStyle)
3345 : {
3346 : // font and mozTextStyle are the same value
3347 0 : return SetFont(textStyle);
3348 : }
3349 :
3350 : NS_IMETHODIMP
3351 0 : nsCanvasRenderingContext2DAzure::GetMozTextStyle(nsAString& textStyle)
3352 : {
3353 : // font and mozTextStyle are the same value
3354 0 : return GetFont(textStyle);
3355 : }
3356 :
3357 0 : gfxFontGroup *nsCanvasRenderingContext2DAzure::GetCurrentFontStyle()
3358 : {
3359 : // use lazy initilization for the font group since it's rather expensive
3360 0 : if (!CurrentState().fontGroup) {
3361 0 : nsresult rv = SetFont(kDefaultFontStyle);
3362 0 : if (NS_FAILED(rv)) {
3363 0 : gfxFontStyle style;
3364 0 : style.size = kDefaultFontSize;
3365 0 : CurrentState().fontGroup =
3366 0 : gfxPlatform::GetPlatform()->CreateFontGroup(kDefaultFontName,
3367 : &style,
3368 0 : nsnull);
3369 0 : if (CurrentState().fontGroup) {
3370 0 : CurrentState().font = kDefaultFontStyle;
3371 0 : rv = NS_OK;
3372 : } else {
3373 0 : rv = NS_ERROR_OUT_OF_MEMORY;
3374 : }
3375 : }
3376 :
3377 0 : NS_ASSERTION(NS_SUCCEEDED(rv), "Default canvas font is invalid");
3378 : }
3379 :
3380 0 : return CurrentState().fontGroup;
3381 : }
3382 :
3383 : //
3384 : // line caps/joins
3385 : //
3386 : NS_IMETHODIMP
3387 0 : nsCanvasRenderingContext2DAzure::SetLineWidth(float width)
3388 : {
3389 0 : if (!FloatValidate(width) || width <= 0.0) {
3390 0 : return NS_OK;
3391 : }
3392 :
3393 0 : CurrentState().lineWidth = width;
3394 0 : return NS_OK;
3395 : }
3396 :
3397 : NS_IMETHODIMP
3398 0 : nsCanvasRenderingContext2DAzure::GetLineWidth(float *width)
3399 : {
3400 0 : *width = CurrentState().lineWidth;
3401 0 : return NS_OK;
3402 : }
3403 :
3404 : NS_IMETHODIMP
3405 0 : nsCanvasRenderingContext2DAzure::SetLineCap(const nsAString& capstyle)
3406 : {
3407 : CapStyle cap;
3408 :
3409 0 : if (capstyle.EqualsLiteral("butt")) {
3410 0 : cap = CAP_BUTT;
3411 0 : } else if (capstyle.EqualsLiteral("round")) {
3412 0 : cap = CAP_ROUND;
3413 0 : } else if (capstyle.EqualsLiteral("square")) {
3414 0 : cap = CAP_SQUARE;
3415 : } else {
3416 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3417 0 : return NS_OK;
3418 : }
3419 :
3420 0 : CurrentState().lineCap = cap;
3421 :
3422 0 : return NS_OK;
3423 : }
3424 :
3425 : NS_IMETHODIMP
3426 0 : nsCanvasRenderingContext2DAzure::GetLineCap(nsAString& capstyle)
3427 : {
3428 0 : switch (CurrentState().lineCap) {
3429 : case CAP_BUTT:
3430 0 : capstyle.AssignLiteral("butt");
3431 0 : break;
3432 : case CAP_ROUND:
3433 0 : capstyle.AssignLiteral("round");
3434 0 : break;
3435 : case CAP_SQUARE:
3436 0 : capstyle.AssignLiteral("square");
3437 0 : break;
3438 : }
3439 :
3440 0 : return NS_OK;
3441 : }
3442 :
3443 : NS_IMETHODIMP
3444 0 : nsCanvasRenderingContext2DAzure::SetLineJoin(const nsAString& joinstyle)
3445 : {
3446 : JoinStyle j;
3447 :
3448 0 : if (joinstyle.EqualsLiteral("round")) {
3449 0 : j = JOIN_ROUND;
3450 0 : } else if (joinstyle.EqualsLiteral("bevel")) {
3451 0 : j = JOIN_BEVEL;
3452 0 : } else if (joinstyle.EqualsLiteral("miter")) {
3453 0 : j = JOIN_MITER_OR_BEVEL;
3454 : } else {
3455 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3456 0 : return NS_OK;
3457 : }
3458 :
3459 0 : CurrentState().lineJoin = j;
3460 :
3461 0 : return NS_OK;
3462 : }
3463 :
3464 : NS_IMETHODIMP
3465 0 : nsCanvasRenderingContext2DAzure::GetLineJoin(nsAString& joinstyle)
3466 : {
3467 0 : switch (CurrentState().lineJoin) {
3468 : case JOIN_ROUND:
3469 0 : joinstyle.AssignLiteral("round");
3470 0 : break;
3471 : case JOIN_BEVEL:
3472 0 : joinstyle.AssignLiteral("bevel");
3473 0 : break;
3474 : case JOIN_MITER_OR_BEVEL:
3475 0 : joinstyle.AssignLiteral("miter");
3476 0 : break;
3477 : default:
3478 0 : return NS_ERROR_FAILURE;
3479 : }
3480 :
3481 0 : return NS_OK;
3482 : }
3483 :
3484 : NS_IMETHODIMP
3485 0 : nsCanvasRenderingContext2DAzure::SetMiterLimit(float miter)
3486 : {
3487 0 : if (!FloatValidate(miter) || miter <= 0.0)
3488 0 : return NS_OK;
3489 :
3490 0 : CurrentState().miterLimit = miter;
3491 :
3492 0 : return NS_OK;
3493 : }
3494 :
3495 : NS_IMETHODIMP
3496 0 : nsCanvasRenderingContext2DAzure::GetMiterLimit(float *miter)
3497 : {
3498 0 : *miter = CurrentState().miterLimit;
3499 0 : return NS_OK;
3500 : }
3501 :
3502 : NS_IMETHODIMP
3503 0 : nsCanvasRenderingContext2DAzure::SetMozDash(JSContext *cx, const jsval& patternArray)
3504 : {
3505 0 : FallibleTArray<Float> dash;
3506 0 : nsresult rv = JSValToDashArray(cx, patternArray, dash);
3507 0 : if (NS_SUCCEEDED(rv)) {
3508 0 : ContextState& state = CurrentState();
3509 0 : state.dash = dash;
3510 0 : if (state.dash.IsEmpty()) {
3511 0 : state.dashOffset = 0;
3512 : }
3513 : }
3514 0 : return rv;
3515 : }
3516 :
3517 : NS_IMETHODIMP
3518 0 : nsCanvasRenderingContext2DAzure::GetMozDash(JSContext* cx, jsval* dashArray)
3519 : {
3520 0 : return DashArrayToJSVal(CurrentState().dash, cx, dashArray);
3521 : }
3522 :
3523 : NS_IMETHODIMP
3524 0 : nsCanvasRenderingContext2DAzure::SetMozDashOffset(float offset)
3525 : {
3526 0 : if (!FloatValidate(offset)) {
3527 0 : return NS_ERROR_ILLEGAL_VALUE;
3528 : }
3529 0 : ContextState& state = CurrentState();
3530 0 : if (!state.dash.IsEmpty()) {
3531 0 : state.dashOffset = offset;
3532 : }
3533 0 : return NS_OK;
3534 : }
3535 :
3536 : NS_IMETHODIMP
3537 0 : nsCanvasRenderingContext2DAzure::GetMozDashOffset(float* offset)
3538 : {
3539 0 : *offset = CurrentState().dashOffset;
3540 0 : return NS_OK;
3541 : }
3542 :
3543 : NS_IMETHODIMP
3544 0 : nsCanvasRenderingContext2DAzure::IsPointInPath(float x, float y, bool *retVal)
3545 : {
3546 0 : if (!FloatValidate(x,y)) {
3547 0 : *retVal = false;
3548 0 : return NS_OK;
3549 : }
3550 :
3551 0 : EnsureUserSpacePath();
3552 :
3553 0 : *retVal = false;
3554 :
3555 0 : if (mPath) {
3556 0 : *retVal = mPath->ContainsPoint(Point(x, y), mTarget->GetTransform());
3557 : }
3558 :
3559 0 : return NS_OK;
3560 : }
3561 :
3562 : //
3563 : // image
3564 : //
3565 :
3566 : // drawImage(in HTMLImageElement image, in float dx, in float dy);
3567 : // -- render image from 0,0 at dx,dy top-left coords
3568 : // drawImage(in HTMLImageElement image, in float dx, in float dy, in float sw, in float sh);
3569 : // -- render image from 0,0 at dx,dy top-left coords clipping it to sw,sh
3570 : // 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);
3571 : // -- render the region defined by (sx,sy,sw,wh) in image-local space into the region (dx,dy,dw,dh) on the canvas
3572 :
3573 : NS_IMETHODIMP
3574 0 : nsCanvasRenderingContext2DAzure::DrawImage(nsIDOMElement *imgElt, float a1,
3575 : float a2, float a3, float a4, float a5,
3576 : float a6, float a7, float a8,
3577 : PRUint8 optional_argc)
3578 : {
3579 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(imgElt);
3580 0 : if (!content) {
3581 0 : return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
3582 : }
3583 :
3584 0 : if (optional_argc == 0) {
3585 0 : if (!FloatValidate(a1, a2)) {
3586 0 : return NS_OK;
3587 : }
3588 0 : } else if (optional_argc == 2) {
3589 0 : if (!FloatValidate(a1, a2, a3, a4)) {
3590 0 : return NS_OK;
3591 : }
3592 0 : } else if (optional_argc == 6) {
3593 0 : if (!FloatValidate(a1, a2, a3, a4, a5, a6) || !FloatValidate(a7, a8)) {
3594 0 : return NS_OK;
3595 : }
3596 : }
3597 :
3598 : double sx,sy,sw,sh;
3599 : double dx,dy,dw,dh;
3600 :
3601 0 : RefPtr<SourceSurface> srcSurf;
3602 0 : gfxIntSize imgSize;
3603 :
3604 0 : nsHTMLCanvasElement* canvas = nsHTMLCanvasElement::FromContent(content);
3605 0 : if (canvas) {
3606 0 : nsIntSize size = canvas->GetSize();
3607 0 : if (size.width == 0 || size.height == 0) {
3608 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
3609 : }
3610 :
3611 : // Special case for Canvas, which could be an Azure canvas!
3612 0 : nsICanvasRenderingContextInternal *srcCanvas = canvas->GetContextAtIndex(0);
3613 0 : if (srcCanvas == this) {
3614 : // Self-copy.
3615 0 : srcSurf = mTarget->Snapshot();
3616 0 : imgSize = gfxIntSize(mWidth, mHeight);
3617 0 : } else if (srcCanvas) {
3618 : // This might not be an Azure canvas!
3619 0 : srcSurf = srcCanvas->GetSurfaceSnapshot();
3620 :
3621 0 : if (srcSurf && mCanvasElement) {
3622 : // Do security check here.
3623 : CanvasUtils::DoDrawImageSecurityCheck(HTMLCanvasElement(),
3624 0 : content->NodePrincipal(), canvas->IsWriteOnly(),
3625 0 : false);
3626 0 : imgSize = gfxIntSize(srcSurf->GetSize().width, srcSurf->GetSize().height);
3627 : }
3628 : }
3629 : } else {
3630 : gfxASurface* imgsurf =
3631 0 : CanvasImageCache::Lookup(imgElt, HTMLCanvasElement(), &imgSize);
3632 0 : if (imgsurf) {
3633 0 : srcSurf = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mTarget, imgsurf);
3634 : }
3635 : }
3636 :
3637 0 : if (!srcSurf) {
3638 : // The canvas spec says that drawImage should draw the first frame
3639 : // of animated images
3640 0 : PRUint32 sfeFlags = nsLayoutUtils::SFE_WANT_FIRST_FRAME;
3641 : nsLayoutUtils::SurfaceFromElementResult res =
3642 0 : nsLayoutUtils::SurfaceFromElement(content->AsElement(), sfeFlags);
3643 :
3644 0 : if (!res.mSurface) {
3645 : // Spec says to silently do nothing if the element is still loading.
3646 0 : return res.mIsStillLoading ? NS_OK : NS_ERROR_NOT_AVAILABLE;
3647 : }
3648 :
3649 : // Ignore cairo surfaces that are bad! See bug 666312.
3650 0 : if (res.mSurface->CairoStatus()) {
3651 0 : return NS_OK;
3652 : }
3653 :
3654 0 : imgSize = res.mSize;
3655 :
3656 0 : if (mCanvasElement) {
3657 : CanvasUtils::DoDrawImageSecurityCheck(HTMLCanvasElement(),
3658 : res.mPrincipal, res.mIsWriteOnly,
3659 0 : res.mCORSUsed);
3660 : }
3661 :
3662 0 : if (res.mImageRequest) {
3663 : CanvasImageCache::NotifyDrawImage(imgElt, HTMLCanvasElement(),
3664 0 : res.mImageRequest, res.mSurface, imgSize);
3665 : }
3666 :
3667 0 : srcSurf = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mTarget, res.mSurface);
3668 : }
3669 :
3670 0 : if (optional_argc == 0) {
3671 0 : dx = a1;
3672 0 : dy = a2;
3673 0 : sx = sy = 0.0;
3674 0 : dw = sw = (double) imgSize.width;
3675 0 : dh = sh = (double) imgSize.height;
3676 0 : } else if (optional_argc == 2) {
3677 0 : dx = a1;
3678 0 : dy = a2;
3679 0 : dw = a3;
3680 0 : dh = a4;
3681 0 : sx = sy = 0.0;
3682 0 : sw = (double) imgSize.width;
3683 0 : sh = (double) imgSize.height;
3684 0 : } else if (optional_argc == 6) {
3685 0 : sx = a1;
3686 0 : sy = a2;
3687 0 : sw = a3;
3688 0 : sh = a4;
3689 0 : dx = a5;
3690 0 : dy = a6;
3691 0 : dw = a7;
3692 0 : dh = a8;
3693 : } else {
3694 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3695 0 : return NS_ERROR_INVALID_ARG;
3696 : }
3697 :
3698 0 : if (dw == 0.0 || dh == 0.0) {
3699 : // not really failure, but nothing to do --
3700 : // and noone likes a divide-by-zero
3701 0 : return NS_OK;
3702 : }
3703 :
3704 0 : if (sx < 0.0 || sy < 0.0 ||
3705 : sw < 0.0 || sw > (double) imgSize.width ||
3706 : sh < 0.0 || sh > (double) imgSize.height ||
3707 : dw < 0.0 || dh < 0.0) {
3708 : // XXX - Unresolved spec issues here, for now return error.
3709 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
3710 : }
3711 :
3712 : Filter filter;
3713 :
3714 0 : if (CurrentState().imageSmoothingEnabled)
3715 0 : filter = mgfx::FILTER_LINEAR;
3716 : else
3717 0 : filter = mgfx::FILTER_POINT;
3718 :
3719 0 : mgfx::Rect bounds;
3720 :
3721 0 : if (NeedToDrawShadow()) {
3722 0 : bounds = mgfx::Rect(dx, dy, dw, dh);
3723 0 : bounds = mTarget->GetTransform().TransformBounds(bounds);
3724 : }
3725 :
3726 0 : AdjustedTarget(this, bounds.IsEmpty() ? nsnull : &bounds)->
3727 : DrawSurface(srcSurf,
3728 : mgfx::Rect(dx, dy, dw, dh),
3729 : mgfx::Rect(sx, sy, sw, sh),
3730 : DrawSurfaceOptions(filter),
3731 0 : DrawOptions(CurrentState().globalAlpha, UsedOperation()));
3732 :
3733 0 : return RedrawUser(gfxRect(dx, dy, dw, dh));
3734 : }
3735 :
3736 : NS_IMETHODIMP
3737 0 : nsCanvasRenderingContext2DAzure::SetGlobalCompositeOperation(const nsAString& op)
3738 : {
3739 : CompositionOp comp_op;
3740 :
3741 : #define CANVAS_OP_TO_GFX_OP(cvsop, op2d) \
3742 : if (op.EqualsLiteral(cvsop)) \
3743 : comp_op = OP_##op2d;
3744 :
3745 0 : CANVAS_OP_TO_GFX_OP("copy", SOURCE)
3746 0 : else CANVAS_OP_TO_GFX_OP("source-atop", ATOP)
3747 0 : else CANVAS_OP_TO_GFX_OP("source-in", IN)
3748 0 : else CANVAS_OP_TO_GFX_OP("source-out", OUT)
3749 0 : else CANVAS_OP_TO_GFX_OP("source-over", OVER)
3750 0 : else CANVAS_OP_TO_GFX_OP("destination-in", DEST_IN)
3751 0 : else CANVAS_OP_TO_GFX_OP("destination-out", DEST_OUT)
3752 0 : else CANVAS_OP_TO_GFX_OP("destination-over", DEST_OVER)
3753 0 : else CANVAS_OP_TO_GFX_OP("destination-atop", DEST_ATOP)
3754 0 : else CANVAS_OP_TO_GFX_OP("lighter", ADD)
3755 0 : else CANVAS_OP_TO_GFX_OP("xor", XOR)
3756 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3757 0 : else return NS_OK;
3758 :
3759 : #undef CANVAS_OP_TO_GFX_OP
3760 0 : CurrentState().op = comp_op;
3761 0 : return NS_OK;
3762 : }
3763 :
3764 : NS_IMETHODIMP
3765 0 : nsCanvasRenderingContext2DAzure::GetGlobalCompositeOperation(nsAString& op)
3766 : {
3767 0 : CompositionOp comp_op = CurrentState().op;
3768 :
3769 : #define CANVAS_OP_TO_GFX_OP(cvsop, op2d) \
3770 : if (comp_op == OP_##op2d) \
3771 : op.AssignLiteral(cvsop);
3772 :
3773 0 : CANVAS_OP_TO_GFX_OP("copy", SOURCE)
3774 0 : else CANVAS_OP_TO_GFX_OP("destination-atop", DEST_ATOP)
3775 0 : else CANVAS_OP_TO_GFX_OP("destination-in", DEST_IN)
3776 0 : else CANVAS_OP_TO_GFX_OP("destination-out", DEST_OUT)
3777 0 : else CANVAS_OP_TO_GFX_OP("destination-over", DEST_OVER)
3778 0 : else CANVAS_OP_TO_GFX_OP("lighter", ADD)
3779 0 : else CANVAS_OP_TO_GFX_OP("source-atop", ATOP)
3780 0 : else CANVAS_OP_TO_GFX_OP("source-in", IN)
3781 0 : else CANVAS_OP_TO_GFX_OP("source-out", OUT)
3782 0 : else CANVAS_OP_TO_GFX_OP("source-over", OVER)
3783 0 : else CANVAS_OP_TO_GFX_OP("xor", XOR)
3784 0 : else return NS_ERROR_FAILURE;
3785 :
3786 : #undef CANVAS_OP_TO_GFX_OP
3787 :
3788 0 : return NS_OK;
3789 : }
3790 :
3791 : NS_IMETHODIMP
3792 0 : nsCanvasRenderingContext2DAzure::DrawWindow(nsIDOMWindow* aWindow, float aX, float aY,
3793 : float aW, float aH,
3794 : const nsAString& aBGColor,
3795 : PRUint32 flags)
3796 : {
3797 0 : NS_ENSURE_ARG(aWindow != nsnull);
3798 :
3799 : // protect against too-large surfaces that will cause allocation
3800 : // or overflow issues
3801 0 : if (!gfxASurface::CheckSurfaceSize(gfxIntSize(PRInt32(aW), PRInt32(aH)),
3802 0 : 0xffff))
3803 0 : return NS_ERROR_FAILURE;
3804 :
3805 0 : nsRefPtr<gfxASurface> drawSurf;
3806 0 : GetThebesSurface(getter_AddRefs(drawSurf));
3807 :
3808 0 : nsRefPtr<gfxContext> thebes = new gfxContext(drawSurf);
3809 :
3810 0 : Matrix matrix = mTarget->GetTransform();
3811 : thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21,
3812 0 : matrix._22, matrix._31, matrix._32));
3813 :
3814 : // We can't allow web apps to call this until we fix at least the
3815 : // following potential security issues:
3816 : // -- rendering cross-domain IFRAMEs and then extracting the results
3817 : // -- rendering the user's theme and then extracting the results
3818 : // -- rendering native anonymous content (e.g., file input paths;
3819 : // scrollbars should be allowed)
3820 0 : if (!nsContentUtils::IsCallerTrustedForRead()) {
3821 : // not permitted to use DrawWindow
3822 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3823 0 : return NS_ERROR_DOM_SECURITY_ERR;
3824 : }
3825 :
3826 : // Flush layout updates
3827 0 : if (!(flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH))
3828 0 : nsContentUtils::FlushLayoutForTree(aWindow);
3829 :
3830 0 : nsRefPtr<nsPresContext> presContext;
3831 0 : nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aWindow);
3832 0 : if (win) {
3833 0 : nsIDocShell* docshell = win->GetDocShell();
3834 0 : if (docshell) {
3835 0 : docshell->GetPresContext(getter_AddRefs(presContext));
3836 : }
3837 : }
3838 0 : if (!presContext)
3839 0 : return NS_ERROR_FAILURE;
3840 :
3841 : nscolor bgColor;
3842 :
3843 : nsIDocument* elementDoc = mCanvasElement ?
3844 0 : HTMLCanvasElement()->OwnerDoc() : nsnull;
3845 :
3846 : // Pass the CSS Loader object to the parser, to allow parser error reports
3847 : // to include the outer window ID.
3848 0 : nsCSSParser parser(elementDoc ? elementDoc->CSSLoader() : nsnull);
3849 0 : nsresult rv = parser.ParseColorString(PromiseFlatString(aBGColor),
3850 0 : nsnull, 0, &bgColor);
3851 0 : NS_ENSURE_SUCCESS(rv, rv);
3852 :
3853 0 : nsIPresShell* presShell = presContext->PresShell();
3854 0 : NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
3855 :
3856 : nsRect r(nsPresContext::CSSPixelsToAppUnits(aX),
3857 : nsPresContext::CSSPixelsToAppUnits(aY),
3858 : nsPresContext::CSSPixelsToAppUnits(aW),
3859 0 : nsPresContext::CSSPixelsToAppUnits(aH));
3860 : PRUint32 renderDocFlags = (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
3861 0 : nsIPresShell::RENDER_DOCUMENT_RELATIVE);
3862 0 : if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_CARET) {
3863 0 : renderDocFlags |= nsIPresShell::RENDER_CARET;
3864 : }
3865 0 : if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_VIEW) {
3866 : renderDocFlags &= ~(nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
3867 0 : nsIPresShell::RENDER_DOCUMENT_RELATIVE);
3868 : }
3869 0 : if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_USE_WIDGET_LAYERS) {
3870 0 : renderDocFlags |= nsIPresShell::RENDER_USE_WIDGET_LAYERS;
3871 : }
3872 0 : if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_ASYNC_DECODE_IMAGES) {
3873 0 : renderDocFlags |= nsIPresShell::RENDER_ASYNC_DECODE_IMAGES;
3874 : }
3875 :
3876 0 : rv = presShell->RenderDocument(r, renderDocFlags, bgColor, thebes);
3877 :
3878 : // note that aX and aY are coordinates in the document that
3879 : // we're drawing; aX and aY are drawn to 0,0 in current user
3880 : // space.
3881 0 : RedrawUser(gfxRect(0, 0, aW, aH));
3882 :
3883 0 : return NS_OK;
3884 : }
3885 :
3886 : NS_IMETHODIMP
3887 0 : nsCanvasRenderingContext2DAzure::AsyncDrawXULElement(nsIDOMXULElement* aElem,
3888 : float aX, float aY,
3889 : float aW, float aH,
3890 : const nsAString& aBGColor,
3891 : PRUint32 flags)
3892 : {
3893 0 : NS_ENSURE_ARG(aElem != nsnull);
3894 :
3895 : // We can't allow web apps to call this until we fix at least the
3896 : // following potential security issues:
3897 : // -- rendering cross-domain IFRAMEs and then extracting the results
3898 : // -- rendering the user's theme and then extracting the results
3899 : // -- rendering native anonymous content (e.g., file input paths;
3900 : // scrollbars should be allowed)
3901 0 : if (!nsContentUtils::IsCallerTrustedForRead()) {
3902 : // not permitted to use DrawWindow
3903 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3904 0 : return NS_ERROR_DOM_SECURITY_ERR;
3905 : }
3906 :
3907 : #if 0
3908 : nsCOMPtr<nsIFrameLoaderOwner> loaderOwner = do_QueryInterface(aElem);
3909 : if (!loaderOwner)
3910 : return NS_ERROR_FAILURE;
3911 :
3912 : nsRefPtr<nsFrameLoader> frameloader = loaderOwner->GetFrameLoader();
3913 : if (!frameloader)
3914 : return NS_ERROR_FAILURE;
3915 :
3916 : PBrowserParent *child = frameloader->GetRemoteBrowser();
3917 : if (!child) {
3918 : nsCOMPtr<nsIDOMWindow> window =
3919 : do_GetInterface(frameloader->GetExistingDocShell());
3920 : if (!window)
3921 : return NS_ERROR_FAILURE;
3922 :
3923 : return DrawWindow(window, aX, aY, aW, aH, aBGColor, flags);
3924 : }
3925 :
3926 : // protect against too-large surfaces that will cause allocation
3927 : // or overflow issues
3928 : if (!gfxASurface::CheckSurfaceSize(gfxIntSize(aW, aH), 0xffff))
3929 : return NS_ERROR_FAILURE;
3930 :
3931 : bool flush =
3932 : (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH) == 0;
3933 :
3934 : PRUint32 renderDocFlags = nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
3935 : if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_CARET) {
3936 : renderDocFlags |= nsIPresShell::RENDER_CARET;
3937 : }
3938 : if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_VIEW) {
3939 : renderDocFlags &= ~nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
3940 : }
3941 :
3942 : nsRect rect(nsPresContext::CSSPixelsToAppUnits(aX),
3943 : nsPresContext::CSSPixelsToAppUnits(aY),
3944 : nsPresContext::CSSPixelsToAppUnits(aW),
3945 : nsPresContext::CSSPixelsToAppUnits(aH));
3946 : if (mIPC) {
3947 : PDocumentRendererParent *pdocrender =
3948 : child->SendPDocumentRendererConstructor(rect,
3949 : mThebes->CurrentMatrix(),
3950 : nsString(aBGColor),
3951 : renderDocFlags, flush,
3952 : nsIntSize(mWidth, mHeight));
3953 : if (!pdocrender)
3954 : return NS_ERROR_FAILURE;
3955 :
3956 : DocumentRendererParent *docrender =
3957 : static_cast<DocumentRendererParent *>(pdocrender);
3958 :
3959 : docrender->SetCanvasContext(this, mThebes);
3960 : }
3961 : #endif
3962 0 : return NS_OK;
3963 : }
3964 :
3965 : //
3966 : // device pixel getting/setting
3967 : //
3968 :
3969 : void
3970 0 : nsCanvasRenderingContext2DAzure::EnsureUnpremultiplyTable() {
3971 0 : if (sUnpremultiplyTable)
3972 0 : return;
3973 :
3974 : // Infallably alloc the unpremultiply table.
3975 0 : sUnpremultiplyTable = new PRUint8[256][256];
3976 :
3977 : // It's important that the array be indexed first by alpha and then by rgb
3978 : // value. When we unpremultiply a pixel, we're guaranteed to do three
3979 : // lookups with the same alpha; indexing by alpha first makes it likely that
3980 : // those three lookups will be close to one another in memory, thus
3981 : // increasing the chance of a cache hit.
3982 :
3983 : // a == 0 case
3984 0 : for (PRUint32 c = 0; c <= 255; c++) {
3985 0 : sUnpremultiplyTable[0][c] = c;
3986 : }
3987 :
3988 0 : for (int a = 1; a <= 255; a++) {
3989 0 : for (int c = 0; c <= 255; c++) {
3990 0 : sUnpremultiplyTable[a][c] = (PRUint8)((c * 255) / a);
3991 : }
3992 : }
3993 : }
3994 :
3995 :
3996 : NS_IMETHODIMP
3997 0 : nsCanvasRenderingContext2DAzure::GetImageData()
3998 : {
3999 : /* Should never be called -- GetImageData_explicit is the QS entry point */
4000 0 : return NS_ERROR_NOT_IMPLEMENTED;
4001 : }
4002 :
4003 : NS_IMETHODIMP
4004 0 : nsCanvasRenderingContext2DAzure::GetImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 w, PRUint32 h,
4005 : PRUint8 *aData, PRUint32 aDataLen)
4006 : {
4007 0 : if (!mValid)
4008 0 : return NS_ERROR_FAILURE;
4009 :
4010 0 : if (!mCanvasElement && !mDocShell) {
4011 0 : NS_ERROR("No canvas element and no docshell in GetImageData!!!");
4012 0 : return NS_ERROR_DOM_SECURITY_ERR;
4013 : }
4014 :
4015 : // Check only if we have a canvas element; if we were created with a docshell,
4016 : // then it's special internal use.
4017 0 : if (mCanvasElement &&
4018 0 : HTMLCanvasElement()->IsWriteOnly() &&
4019 0 : !nsContentUtils::IsCallerTrustedForRead())
4020 : {
4021 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
4022 0 : return NS_ERROR_DOM_SECURITY_ERR;
4023 : }
4024 :
4025 0 : if (w == 0 || h == 0 || aDataLen != w * h * 4)
4026 0 : return NS_ERROR_DOM_SYNTAX_ERR;
4027 :
4028 0 : CheckedInt32 rightMost = CheckedInt32(x) + w;
4029 0 : CheckedInt32 bottomMost = CheckedInt32(y) + h;
4030 :
4031 0 : if (!rightMost.valid() || !bottomMost.valid())
4032 0 : return NS_ERROR_DOM_SYNTAX_ERR;
4033 :
4034 0 : if (mZero) {
4035 0 : return NS_OK;
4036 : }
4037 :
4038 0 : IntRect srcRect(0, 0, mWidth, mHeight);
4039 0 : IntRect destRect(x, y, w, h);
4040 :
4041 0 : if (!srcRect.Contains(destRect)) {
4042 : // Some data is outside the canvas surface, clear the destination.
4043 0 : memset(aData, 0, aDataLen);
4044 : }
4045 :
4046 0 : IntRect srcReadRect = srcRect.Intersect(destRect);
4047 0 : IntRect dstWriteRect = srcReadRect;
4048 0 : dstWriteRect.MoveBy(-x, -y);
4049 :
4050 0 : PRUint8 *src = aData;
4051 0 : PRUint32 srcStride = w * 4;
4052 :
4053 0 : RefPtr<DataSourceSurface> readback;
4054 0 : if (!srcReadRect.IsEmpty()) {
4055 0 : RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
4056 :
4057 0 : readback = snapshot->GetDataSurface();
4058 :
4059 0 : srcStride = readback->Stride();
4060 0 : src = readback->GetData() + srcReadRect.y * srcStride + srcReadRect.x * 4;
4061 : }
4062 :
4063 : // make sure sUnpremultiplyTable has been created
4064 0 : EnsureUnpremultiplyTable();
4065 :
4066 : // NOTE! dst is the same as src, and this relies on reading
4067 : // from src and advancing that ptr before writing to dst.
4068 0 : PRUint8 *dst = aData + dstWriteRect.y * (w * 4) + dstWriteRect.x * 4;
4069 :
4070 0 : for (int j = 0; j < dstWriteRect.height; j++) {
4071 0 : for (int i = 0; i < dstWriteRect.width; i++) {
4072 : // XXX Is there some useful swizzle MMX we can use here?
4073 : #ifdef IS_LITTLE_ENDIAN
4074 0 : PRUint8 b = *src++;
4075 0 : PRUint8 g = *src++;
4076 0 : PRUint8 r = *src++;
4077 0 : PRUint8 a = *src++;
4078 : #else
4079 : PRUint8 a = *src++;
4080 : PRUint8 r = *src++;
4081 : PRUint8 g = *src++;
4082 : PRUint8 b = *src++;
4083 : #endif
4084 : // Convert to non-premultiplied color
4085 0 : *dst++ = sUnpremultiplyTable[a][r];
4086 0 : *dst++ = sUnpremultiplyTable[a][g];
4087 0 : *dst++ = sUnpremultiplyTable[a][b];
4088 0 : *dst++ = a;
4089 : }
4090 0 : src += srcStride - (dstWriteRect.width * 4);
4091 0 : dst += (w * 4) - (dstWriteRect.width * 4);
4092 : }
4093 0 : return NS_OK;
4094 : }
4095 :
4096 : void
4097 0 : nsCanvasRenderingContext2DAzure::EnsurePremultiplyTable() {
4098 0 : if (sPremultiplyTable)
4099 0 : return;
4100 :
4101 : // Infallably alloc the premultiply table.
4102 0 : sPremultiplyTable = new PRUint8[256][256];
4103 :
4104 : // Like the unpremultiply table, it's important that we index the premultiply
4105 : // table with the alpha value as the first index to ensure good cache
4106 : // performance.
4107 :
4108 0 : for (int a = 0; a <= 255; a++) {
4109 0 : for (int c = 0; c <= 255; c++) {
4110 0 : sPremultiplyTable[a][c] = (a * c + 254) / 255;
4111 : }
4112 : }
4113 : }
4114 :
4115 : void
4116 0 : nsCanvasRenderingContext2DAzure::FillRuleChanged()
4117 : {
4118 0 : if (mPath) {
4119 0 : mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
4120 0 : mPath = nsnull;
4121 : }
4122 0 : }
4123 :
4124 : // void putImageData (in ImageData d, in float x, in float y);
4125 : NS_IMETHODIMP
4126 0 : nsCanvasRenderingContext2DAzure::PutImageData()
4127 : {
4128 : /* Should never be called -- PutImageData_explicit is the QS entry point */
4129 0 : return NS_ERROR_NOT_IMPLEMENTED;
4130 : }
4131 :
4132 : NS_IMETHODIMP
4133 0 : nsCanvasRenderingContext2DAzure::PutImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 w, PRUint32 h,
4134 : unsigned char *aData, PRUint32 aDataLen,
4135 : bool hasDirtyRect, PRInt32 dirtyX, PRInt32 dirtyY,
4136 : PRInt32 dirtyWidth, PRInt32 dirtyHeight)
4137 : {
4138 0 : if (!mValid) {
4139 0 : return NS_ERROR_FAILURE;
4140 : }
4141 :
4142 0 : if (w == 0 || h == 0) {
4143 0 : return NS_ERROR_DOM_SYNTAX_ERR;
4144 : }
4145 :
4146 0 : IntRect dirtyRect;
4147 0 : IntRect imageDataRect(0, 0, w, h);
4148 :
4149 0 : if (hasDirtyRect) {
4150 : // fix up negative dimensions
4151 0 : if (dirtyWidth < 0) {
4152 0 : NS_ENSURE_TRUE(dirtyWidth != INT_MIN, NS_ERROR_DOM_INDEX_SIZE_ERR);
4153 :
4154 0 : CheckedInt32 checkedDirtyX = CheckedInt32(dirtyX) + dirtyWidth;
4155 :
4156 0 : if (!checkedDirtyX.valid())
4157 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
4158 :
4159 0 : dirtyX = checkedDirtyX.value();
4160 0 : dirtyWidth = -(int32)dirtyWidth;
4161 : }
4162 :
4163 0 : if (dirtyHeight < 0) {
4164 0 : NS_ENSURE_TRUE(dirtyHeight != INT_MIN, NS_ERROR_DOM_INDEX_SIZE_ERR);
4165 :
4166 0 : CheckedInt32 checkedDirtyY = CheckedInt32(dirtyY) + dirtyHeight;
4167 :
4168 0 : if (!checkedDirtyY.valid())
4169 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
4170 :
4171 0 : dirtyY = checkedDirtyY.value();
4172 0 : dirtyHeight = -(int32)dirtyHeight;
4173 : }
4174 :
4175 : // bound the dirty rect within the imageData rectangle
4176 0 : dirtyRect = imageDataRect.Intersect(IntRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight));
4177 :
4178 0 : if (dirtyRect.Width() <= 0 || dirtyRect.Height() <= 0)
4179 0 : return NS_OK;
4180 : } else {
4181 0 : dirtyRect = imageDataRect;
4182 : }
4183 :
4184 0 : dirtyRect.MoveBy(IntPoint(x, y));
4185 0 : dirtyRect = IntRect(0, 0, mWidth, mHeight).Intersect(dirtyRect);
4186 :
4187 0 : if (dirtyRect.Width() <= 0 || dirtyRect.Height() <= 0) {
4188 0 : return NS_OK;
4189 : }
4190 :
4191 0 : PRUint32 len = w * h * 4;
4192 0 : if (aDataLen != len) {
4193 0 : return NS_ERROR_DOM_SYNTAX_ERR;
4194 : }
4195 :
4196 : nsRefPtr<gfxImageSurface> imgsurf = new gfxImageSurface(gfxIntSize(w, h),
4197 0 : gfxASurface::ImageFormatARGB32);
4198 0 : if (!imgsurf || imgsurf->CairoStatus()) {
4199 0 : return NS_ERROR_FAILURE;
4200 : }
4201 :
4202 : // ensure premultiply table has been created
4203 0 : EnsurePremultiplyTable();
4204 :
4205 0 : PRUint8 *src = aData;
4206 0 : PRUint8 *dst = imgsurf->Data();
4207 :
4208 0 : for (PRUint32 j = 0; j < h; j++) {
4209 0 : for (PRUint32 i = 0; i < w; i++) {
4210 0 : PRUint8 r = *src++;
4211 0 : PRUint8 g = *src++;
4212 0 : PRUint8 b = *src++;
4213 0 : PRUint8 a = *src++;
4214 :
4215 : // Convert to premultiplied color (losslessly if the input came from getImageData)
4216 : #ifdef IS_LITTLE_ENDIAN
4217 0 : *dst++ = sPremultiplyTable[a][b];
4218 0 : *dst++ = sPremultiplyTable[a][g];
4219 0 : *dst++ = sPremultiplyTable[a][r];
4220 0 : *dst++ = a;
4221 : #else
4222 : *dst++ = a;
4223 : *dst++ = sPremultiplyTable[a][r];
4224 : *dst++ = sPremultiplyTable[a][g];
4225 : *dst++ = sPremultiplyTable[a][b];
4226 : #endif
4227 : }
4228 : }
4229 :
4230 : RefPtr<SourceSurface> sourceSurface =
4231 0 : mTarget->CreateSourceSurfaceFromData(imgsurf->Data(), IntSize(w, h), imgsurf->Stride(), FORMAT_B8G8R8A8);
4232 :
4233 :
4234 0 : mTarget->CopySurface(sourceSurface,
4235 : IntRect(dirtyRect.x - x, dirtyRect.y - y,
4236 : dirtyRect.width, dirtyRect.height),
4237 0 : IntPoint(dirtyRect.x, dirtyRect.y));
4238 :
4239 0 : Redraw(mgfx::Rect(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height));
4240 :
4241 0 : return NS_OK;
4242 : }
4243 :
4244 : NS_IMETHODIMP
4245 0 : nsCanvasRenderingContext2DAzure::GetThebesSurface(gfxASurface **surface)
4246 : {
4247 0 : if (!mTarget) {
4248 : nsRefPtr<gfxASurface> tmpSurf =
4249 0 : gfxPlatform::GetPlatform()->CreateOffscreenSurface(gfxIntSize(1, 1), gfxASurface::CONTENT_COLOR_ALPHA);
4250 0 : *surface = tmpSurf.forget().get();
4251 0 : return NS_OK;
4252 : }
4253 :
4254 0 : if (!mThebesSurface) {
4255 : mThebesSurface =
4256 0 : gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
4257 :
4258 0 : if (!mThebesSurface) {
4259 0 : return NS_ERROR_FAILURE;
4260 : }
4261 : } else {
4262 : // Normally GetThebesSurfaceForDrawTarget will handle the flush, when
4263 : // we're returning a cached ThebesSurface we need to flush here.
4264 0 : mTarget->Flush();
4265 : }
4266 :
4267 0 : *surface = mThebesSurface;
4268 0 : NS_ADDREF(*surface);
4269 :
4270 0 : return NS_OK;
4271 : }
4272 :
4273 : NS_IMETHODIMP
4274 0 : nsCanvasRenderingContext2DAzure::CreateImageData()
4275 : {
4276 : /* Should never be called; handled entirely in the quickstub */
4277 0 : return NS_ERROR_NOT_IMPLEMENTED;
4278 : }
4279 :
4280 : NS_IMETHODIMP
4281 0 : nsCanvasRenderingContext2DAzure::GetMozImageSmoothingEnabled(bool *retVal)
4282 : {
4283 0 : *retVal = CurrentState().imageSmoothingEnabled;
4284 0 : return NS_OK;
4285 : }
4286 :
4287 : NS_IMETHODIMP
4288 0 : nsCanvasRenderingContext2DAzure::SetMozImageSmoothingEnabled(bool val)
4289 : {
4290 0 : if (val != CurrentState().imageSmoothingEnabled) {
4291 0 : CurrentState().imageSmoothingEnabled = val;
4292 : }
4293 :
4294 0 : return NS_OK;
4295 : }
4296 :
4297 : static PRUint8 g2DContextLayerUserData;
4298 :
4299 0 : class CanvasRenderingContext2DUserData : public LayerUserData {
4300 : public:
4301 0 : CanvasRenderingContext2DUserData(nsHTMLCanvasElement *aContent)
4302 0 : : mContent(aContent) {}
4303 0 : static void DidTransactionCallback(void* aData)
4304 : {
4305 0 : static_cast<CanvasRenderingContext2DUserData*>(aData)->mContent->MarkContextClean();
4306 0 : }
4307 :
4308 : private:
4309 : nsRefPtr<nsHTMLCanvasElement> mContent;
4310 : };
4311 :
4312 : already_AddRefed<CanvasLayer>
4313 0 : nsCanvasRenderingContext2DAzure::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
4314 : CanvasLayer *aOldLayer,
4315 : LayerManager *aManager)
4316 : {
4317 0 : if (!mValid) {
4318 0 : return nsnull;
4319 : }
4320 :
4321 0 : if (mTarget) {
4322 0 : mTarget->Flush();
4323 : }
4324 :
4325 0 : if (!mResetLayer && aOldLayer &&
4326 0 : aOldLayer->HasUserData(&g2DContextLayerUserData)) {
4327 0 : NS_ADDREF(aOldLayer);
4328 0 : return aOldLayer;
4329 : }
4330 :
4331 0 : nsRefPtr<CanvasLayer> canvasLayer = aManager->CreateCanvasLayer();
4332 0 : if (!canvasLayer) {
4333 0 : NS_WARNING("CreateCanvasLayer returned null!");
4334 0 : return nsnull;
4335 : }
4336 0 : CanvasRenderingContext2DUserData *userData = nsnull;
4337 0 : if (aBuilder->IsPaintingToWindow()) {
4338 : // Make the layer tell us whenever a transaction finishes (including
4339 : // the current transaction), so we can clear our invalidation state and
4340 : // start invalidating again. We need to do this for the layer that is
4341 : // being painted to a window (there shouldn't be more than one at a time,
4342 : // and if there is, flushing the invalidation state more often than
4343 : // necessary is harmless).
4344 :
4345 : // The layer will be destroyed when we tear down the presentation
4346 : // (at the latest), at which time this userData will be destroyed,
4347 : // releasing the reference to the element.
4348 : // The userData will receive DidTransactionCallbacks, which flush the
4349 : // the invalidation state to indicate that the canvas is up to date.
4350 0 : userData = new CanvasRenderingContext2DUserData(HTMLCanvasElement());
4351 : canvasLayer->SetDidTransactionCallback(
4352 0 : CanvasRenderingContext2DUserData::DidTransactionCallback, userData);
4353 : }
4354 0 : canvasLayer->SetUserData(&g2DContextLayerUserData, userData);
4355 :
4356 0 : CanvasLayer::Data data;
4357 :
4358 0 : data.mDrawTarget = mTarget;
4359 0 : data.mSize = nsIntSize(mWidth, mHeight);
4360 :
4361 0 : canvasLayer->Initialize(data);
4362 0 : PRUint32 flags = mOpaque ? Layer::CONTENT_OPAQUE : 0;
4363 0 : canvasLayer->SetContentFlags(flags);
4364 0 : canvasLayer->Updated();
4365 :
4366 0 : mResetLayer = false;
4367 :
4368 0 : return canvasLayer.forget();
4369 : }
4370 :
4371 : void
4372 0 : nsCanvasRenderingContext2DAzure::MarkContextClean()
4373 : {
4374 0 : if (mInvalidateCount > 0) {
4375 0 : mPredictManyRedrawCalls = mInvalidateCount > kCanvasMaxInvalidateCount;
4376 : }
4377 0 : mIsEntireFrameInvalid = false;
4378 0 : mInvalidateCount = 0;
4379 4392 : }
4380 :
|