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 1396 : static NS_NAMED_LITERAL_STRING(kDefaultFontName, "sans-serif");
139 1396 : 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 1396 : 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 1396 : 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 : }
2545 : }
2546 :
2547 : void
2548 0 : nsCanvasRenderingContext2DAzure::EnsureUserSpacePath()
2549 : {
2550 0 : FillRule fillRule = CurrentState().fillRule;
2551 :
2552 0 : if (!mPath && !mPathBuilder && !mDSPathBuilder) {
2553 0 : mPathBuilder = mTarget->CreatePathBuilder(fillRule);
2554 : }
2555 :
2556 0 : if (mPathBuilder) {
2557 0 : mPath = mPathBuilder->Finish();
2558 0 : mPathBuilder = nsnull;
2559 : }
2560 :
2561 0 : if (mPath && mPathTransformWillUpdate) {
2562 : mDSPathBuilder =
2563 0 : mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
2564 0 : mPath = nsnull;
2565 0 : mPathTransformWillUpdate = false;
2566 : }
2567 :
2568 0 : if (mDSPathBuilder) {
2569 0 : RefPtr<Path> dsPath;
2570 0 : dsPath = mDSPathBuilder->Finish();
2571 0 : mDSPathBuilder = nsnull;
2572 :
2573 0 : Matrix inverse = mTarget->GetTransform();
2574 0 : if (!inverse.Invert()) {
2575 : return;
2576 : }
2577 :
2578 : mPathBuilder =
2579 0 : dsPath->TransformedCopyToBuilder(inverse, fillRule);
2580 0 : mPath = mPathBuilder->Finish();
2581 0 : mPathBuilder = nsnull;
2582 : }
2583 :
2584 0 : if (mPath && mPath->GetFillRule() != fillRule) {
2585 0 : mPathBuilder = mPath->CopyToBuilder(fillRule);
2586 0 : mPath = mPathBuilder->Finish();
2587 : }
2588 : }
2589 :
2590 : void
2591 0 : nsCanvasRenderingContext2DAzure::TransformWillUpdate()
2592 : {
2593 : // Store the matrix that would transform the current path to device
2594 : // space.
2595 0 : if (mPath || mPathBuilder) {
2596 0 : if (!mPathTransformWillUpdate) {
2597 : // If the transform has already been updated, but a device space builder
2598 : // has not been created yet mPathToDS contains the right transform to
2599 : // transform the current mPath into device space.
2600 : // We should leave it alone.
2601 0 : mPathToDS = mTarget->GetTransform();
2602 : }
2603 0 : mPathTransformWillUpdate = true;
2604 : }
2605 0 : }
2606 :
2607 : //
2608 : // text
2609 : //
2610 :
2611 : /**
2612 : * Helper function for SetFont that creates a style rule for the given font.
2613 : * @param aFont The CSS font string
2614 : * @param aNode The canvas element
2615 : * @param aResult Pointer in which to place the new style rule.
2616 : * @remark Assumes all pointer arguments are non-null.
2617 : */
2618 : static nsresult
2619 0 : CreateFontStyleRule(const nsAString& aFont,
2620 : nsINode* aNode,
2621 : StyleRule** aResult)
2622 : {
2623 0 : nsRefPtr<StyleRule> rule;
2624 : bool changed;
2625 :
2626 0 : nsIPrincipal* principal = aNode->NodePrincipal();
2627 0 : nsIDocument* document = aNode->OwnerDoc();
2628 :
2629 0 : nsIURI* docURL = document->GetDocumentURI();
2630 0 : nsIURI* baseURL = document->GetDocBaseURI();
2631 :
2632 : // Pass the CSS Loader object to the parser, to allow parser error reports
2633 : // to include the outer window ID.
2634 0 : nsCSSParser parser(document->CSSLoader());
2635 :
2636 0 : nsresult rv = parser.ParseStyleAttribute(EmptyString(), docURL, baseURL,
2637 0 : principal, getter_AddRefs(rule));
2638 0 : if (NS_FAILED(rv)) {
2639 0 : return rv;
2640 : }
2641 :
2642 : rv = parser.ParseProperty(eCSSProperty_font, aFont, docURL, baseURL,
2643 : principal, rule->GetDeclaration(), &changed,
2644 0 : false);
2645 0 : if (NS_FAILED(rv))
2646 0 : return rv;
2647 :
2648 : rv = parser.ParseProperty(eCSSProperty_line_height,
2649 0 : NS_LITERAL_STRING("normal"), docURL, baseURL,
2650 : principal, rule->GetDeclaration(), &changed,
2651 0 : false);
2652 0 : if (NS_FAILED(rv)) {
2653 0 : return rv;
2654 : }
2655 :
2656 0 : rule->RuleMatched();
2657 :
2658 0 : rule.forget(aResult);
2659 0 : return NS_OK;
2660 : }
2661 :
2662 : NS_IMETHODIMP
2663 0 : nsCanvasRenderingContext2DAzure::SetFont(const nsAString& font)
2664 : {
2665 : nsresult rv;
2666 :
2667 : /*
2668 : * If font is defined with relative units (e.g. ems) and the parent
2669 : * style context changes in between calls, setting the font to the
2670 : * same value as previous could result in a different computed value,
2671 : * so we cannot have the optimization where we check if the new font
2672 : * string is equal to the old one.
2673 : */
2674 :
2675 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mCanvasElement);
2676 0 : if (!content && !mDocShell) {
2677 0 : NS_WARNING("Canvas element must be an nsIContent and non-null or a docshell must be provided");
2678 0 : return NS_ERROR_FAILURE;
2679 : }
2680 :
2681 0 : nsIPresShell* presShell = GetPresShell();
2682 0 : if (!presShell) {
2683 0 : return NS_ERROR_FAILURE;
2684 : }
2685 0 : nsIDocument* document = presShell->GetDocument();
2686 :
2687 0 : nsCOMArray<nsIStyleRule> rules;
2688 :
2689 0 : nsRefPtr<css::StyleRule> rule;
2690 0 : rv = CreateFontStyleRule(font, document, getter_AddRefs(rule));
2691 :
2692 0 : if (NS_FAILED(rv)) {
2693 0 : return rv;
2694 : }
2695 :
2696 0 : css::Declaration *declaration = rule->GetDeclaration();
2697 : // The easiest way to see whether we got a syntax error or whether
2698 : // we got 'inherit' or 'initial' is to look at font-size-adjust,
2699 : // which the shorthand resets to either 'none' or
2700 : // '-moz-system-font'.
2701 : // We know the declaration is not !important, so we can use
2702 : // GetNormalBlock().
2703 : const nsCSSValue *fsaVal =
2704 0 : declaration->GetNormalBlock()->ValueFor(eCSSProperty_font_size_adjust);
2705 0 : if (!fsaVal || (fsaVal->GetUnit() != eCSSUnit_None &&
2706 0 : fsaVal->GetUnit() != eCSSUnit_System_Font)) {
2707 : // We got an all-property value or a syntax error. The spec says
2708 : // this value must be ignored.
2709 0 : return NS_OK;
2710 : }
2711 :
2712 0 : rules.AppendObject(rule);
2713 :
2714 0 : nsStyleSet* styleSet = presShell->StyleSet();
2715 :
2716 : // have to get a parent style context for inherit-like relative
2717 : // values (2em, bolder, etc.)
2718 0 : nsRefPtr<nsStyleContext> parentContext;
2719 :
2720 0 : if (content && content->IsInDoc()) {
2721 : // inherit from the canvas element
2722 : parentContext = nsComputedDOMStyle::GetStyleContextForElement(
2723 0 : content->AsElement(),
2724 : nsnull,
2725 0 : presShell);
2726 : } else {
2727 : // otherwise inherit from default (10px sans-serif)
2728 0 : nsRefPtr<css::StyleRule> parentRule;
2729 0 : rv = CreateFontStyleRule(NS_LITERAL_STRING("10px sans-serif"),
2730 : document,
2731 0 : getter_AddRefs(parentRule));
2732 :
2733 0 : if (NS_FAILED(rv)) {
2734 0 : return rv;
2735 : }
2736 :
2737 0 : nsCOMArray<nsIStyleRule> parentRules;
2738 0 : parentRules.AppendObject(parentRule);
2739 0 : parentContext = styleSet->ResolveStyleForRules(nsnull, parentRules);
2740 : }
2741 :
2742 0 : if (!parentContext) {
2743 0 : return NS_ERROR_FAILURE;
2744 : }
2745 :
2746 : nsRefPtr<nsStyleContext> sc =
2747 0 : styleSet->ResolveStyleForRules(parentContext, rules);
2748 0 : if (!sc) {
2749 0 : return NS_ERROR_FAILURE;
2750 : }
2751 :
2752 0 : const nsStyleFont* fontStyle = sc->GetStyleFont();
2753 :
2754 0 : NS_ASSERTION(fontStyle, "Could not obtain font style");
2755 :
2756 0 : nsIAtom* language = sc->GetStyleFont()->mLanguage;
2757 0 : if (!language) {
2758 0 : language = presShell->GetPresContext()->GetLanguageFromCharset();
2759 : }
2760 :
2761 : // use CSS pixels instead of dev pixels to avoid being affected by page zoom
2762 0 : const PRUint32 aupcp = nsPresContext::AppUnitsPerCSSPixel();
2763 : // un-zoom the font size to avoid being affected by text-only zoom
2764 : //
2765 : // Purposely ignore the font size that respects the user's minimum
2766 : // font preference (fontStyle->mFont.size) in favor of the computed
2767 : // size (fontStyle->mSize). See
2768 : // https://bugzilla.mozilla.org/show_bug.cgi?id=698652.
2769 0 : const nscoord fontSize = nsStyleFont::UnZoomText(parentContext->PresContext(), fontStyle->mSize);
2770 :
2771 0 : bool printerFont = (presShell->GetPresContext()->Type() == nsPresContext::eContext_PrintPreview ||
2772 0 : presShell->GetPresContext()->Type() == nsPresContext::eContext_Print);
2773 :
2774 : gfxFontStyle style(fontStyle->mFont.style,
2775 : fontStyle->mFont.weight,
2776 : fontStyle->mFont.stretch,
2777 0 : NSAppUnitsToFloatPixels(fontSize, float(aupcp)),
2778 : language,
2779 : fontStyle->mFont.sizeAdjust,
2780 : fontStyle->mFont.systemFont,
2781 : printerFont,
2782 : fontStyle->mFont.featureSettings,
2783 0 : fontStyle->mFont.languageOverride);
2784 :
2785 0 : CurrentState().fontGroup =
2786 0 : gfxPlatform::GetPlatform()->CreateFontGroup(fontStyle->mFont.name,
2787 : &style,
2788 0 : presShell->GetPresContext()->GetUserFontSet());
2789 0 : NS_ASSERTION(CurrentState().fontGroup, "Could not get font group");
2790 :
2791 : // The font getter is required to be reserialized based on what we
2792 : // parsed (including having line-height removed). (Older drafts of
2793 : // the spec required font sizes be converted to pixels, but that no
2794 : // longer seems to be required.)
2795 0 : declaration->GetValue(eCSSProperty_font, CurrentState().font);
2796 :
2797 0 : return NS_OK;
2798 : }
2799 :
2800 : NS_IMETHODIMP
2801 0 : nsCanvasRenderingContext2DAzure::GetFont(nsAString& font)
2802 : {
2803 : /* will initilize the value if not set, else does nothing */
2804 0 : GetCurrentFontStyle();
2805 :
2806 0 : font = CurrentState().font;
2807 0 : return NS_OK;
2808 : }
2809 :
2810 : NS_IMETHODIMP
2811 0 : nsCanvasRenderingContext2DAzure::SetTextAlign(const nsAString& ta)
2812 : {
2813 0 : if (ta.EqualsLiteral("start"))
2814 0 : CurrentState().textAlign = TEXT_ALIGN_START;
2815 0 : else if (ta.EqualsLiteral("end"))
2816 0 : CurrentState().textAlign = TEXT_ALIGN_END;
2817 0 : else if (ta.EqualsLiteral("left"))
2818 0 : CurrentState().textAlign = TEXT_ALIGN_LEFT;
2819 0 : else if (ta.EqualsLiteral("right"))
2820 0 : CurrentState().textAlign = TEXT_ALIGN_RIGHT;
2821 0 : else if (ta.EqualsLiteral("center"))
2822 0 : CurrentState().textAlign = TEXT_ALIGN_CENTER;
2823 :
2824 0 : return NS_OK;
2825 : }
2826 :
2827 : NS_IMETHODIMP
2828 0 : nsCanvasRenderingContext2DAzure::GetTextAlign(nsAString& ta)
2829 : {
2830 0 : switch (CurrentState().textAlign)
2831 : {
2832 : case TEXT_ALIGN_START:
2833 0 : ta.AssignLiteral("start");
2834 0 : break;
2835 : case TEXT_ALIGN_END:
2836 0 : ta.AssignLiteral("end");
2837 0 : break;
2838 : case TEXT_ALIGN_LEFT:
2839 0 : ta.AssignLiteral("left");
2840 0 : break;
2841 : case TEXT_ALIGN_RIGHT:
2842 0 : ta.AssignLiteral("right");
2843 0 : break;
2844 : case TEXT_ALIGN_CENTER:
2845 0 : ta.AssignLiteral("center");
2846 0 : break;
2847 : }
2848 :
2849 0 : return NS_OK;
2850 : }
2851 :
2852 : NS_IMETHODIMP
2853 0 : nsCanvasRenderingContext2DAzure::SetTextBaseline(const nsAString& tb)
2854 : {
2855 0 : if (tb.EqualsLiteral("top"))
2856 0 : CurrentState().textBaseline = TEXT_BASELINE_TOP;
2857 0 : else if (tb.EqualsLiteral("hanging"))
2858 0 : CurrentState().textBaseline = TEXT_BASELINE_HANGING;
2859 0 : else if (tb.EqualsLiteral("middle"))
2860 0 : CurrentState().textBaseline = TEXT_BASELINE_MIDDLE;
2861 0 : else if (tb.EqualsLiteral("alphabetic"))
2862 0 : CurrentState().textBaseline = TEXT_BASELINE_ALPHABETIC;
2863 0 : else if (tb.EqualsLiteral("ideographic"))
2864 0 : CurrentState().textBaseline = TEXT_BASELINE_IDEOGRAPHIC;
2865 0 : else if (tb.EqualsLiteral("bottom"))
2866 0 : CurrentState().textBaseline = TEXT_BASELINE_BOTTOM;
2867 :
2868 0 : return NS_OK;
2869 : }
2870 :
2871 : NS_IMETHODIMP
2872 0 : nsCanvasRenderingContext2DAzure::GetTextBaseline(nsAString& tb)
2873 : {
2874 0 : switch (CurrentState().textBaseline)
2875 : {
2876 : case TEXT_BASELINE_TOP:
2877 0 : tb.AssignLiteral("top");
2878 0 : break;
2879 : case TEXT_BASELINE_HANGING:
2880 0 : tb.AssignLiteral("hanging");
2881 0 : break;
2882 : case TEXT_BASELINE_MIDDLE:
2883 0 : tb.AssignLiteral("middle");
2884 0 : break;
2885 : case TEXT_BASELINE_ALPHABETIC:
2886 0 : tb.AssignLiteral("alphabetic");
2887 0 : break;
2888 : case TEXT_BASELINE_IDEOGRAPHIC:
2889 0 : tb.AssignLiteral("ideographic");
2890 0 : break;
2891 : case TEXT_BASELINE_BOTTOM:
2892 0 : tb.AssignLiteral("bottom");
2893 0 : break;
2894 : }
2895 :
2896 0 : return NS_OK;
2897 : }
2898 :
2899 : /*
2900 : * Helper function that replaces the whitespace characters in a string
2901 : * with U+0020 SPACE. The whitespace characters are defined as U+0020 SPACE,
2902 : * U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF), U+000B LINE
2903 : * TABULATION, U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR).
2904 : * @param str The string whose whitespace characters to replace.
2905 : */
2906 : static inline void
2907 0 : TextReplaceWhitespaceCharacters(nsAutoString& str)
2908 : {
2909 0 : str.ReplaceChar("\x09\x0A\x0B\x0C\x0D", PRUnichar(' '));
2910 0 : }
2911 :
2912 : NS_IMETHODIMP
2913 0 : nsCanvasRenderingContext2DAzure::FillText(const nsAString& text, float x, float y, float maxWidth)
2914 : {
2915 0 : return DrawOrMeasureText(text, x, y, maxWidth, TEXT_DRAW_OPERATION_FILL, nsnull);
2916 : }
2917 :
2918 : NS_IMETHODIMP
2919 0 : nsCanvasRenderingContext2DAzure::StrokeText(const nsAString& text, float x, float y, float maxWidth)
2920 : {
2921 0 : return DrawOrMeasureText(text, x, y, maxWidth, TEXT_DRAW_OPERATION_STROKE, nsnull);
2922 : }
2923 :
2924 : NS_IMETHODIMP
2925 0 : nsCanvasRenderingContext2DAzure::MeasureText(const nsAString& rawText,
2926 : nsIDOMTextMetrics** _retval)
2927 : {
2928 : float width;
2929 :
2930 0 : nsresult rv = DrawOrMeasureText(rawText, 0, 0, 0, TEXT_DRAW_OPERATION_MEASURE, &width);
2931 :
2932 0 : if (NS_FAILED(rv)) {
2933 0 : return rv;
2934 : }
2935 :
2936 0 : nsRefPtr<nsIDOMTextMetrics> textMetrics = new nsTextMetricsAzure(width);
2937 0 : if (!textMetrics.get()) {
2938 0 : return NS_ERROR_OUT_OF_MEMORY;
2939 : }
2940 :
2941 0 : *_retval = textMetrics.forget().get();
2942 :
2943 0 : return NS_OK;
2944 : }
2945 :
2946 : /**
2947 : * Used for nsBidiPresUtils::ProcessText
2948 : */
2949 : struct NS_STACK_CLASS nsCanvasBidiProcessorAzure : public nsBidiPresUtils::BidiProcessor
2950 0 : {
2951 : typedef nsCanvasRenderingContext2DAzure::ContextState ContextState;
2952 :
2953 0 : virtual void SetText(const PRUnichar* text, PRInt32 length, nsBidiDirection direction)
2954 : {
2955 0 : mFontgrp->UpdateFontList(); // ensure user font generation is current
2956 : mTextRun = mFontgrp->MakeTextRun(text,
2957 : length,
2958 : mThebes,
2959 : mAppUnitsPerDevPixel,
2960 0 : direction==NSBIDI_RTL ? gfxTextRunFactory::TEXT_IS_RTL : 0);
2961 0 : }
2962 :
2963 0 : virtual nscoord GetWidth()
2964 : {
2965 : gfxTextRun::Metrics textRunMetrics = mTextRun->MeasureText(0,
2966 : mTextRun->GetLength(),
2967 : mDoMeasureBoundingBox ?
2968 : gfxFont::TIGHT_INK_EXTENTS :
2969 : gfxFont::LOOSE_INK_EXTENTS,
2970 : mThebes,
2971 0 : nsnull);
2972 :
2973 : // this only measures the height; the total width is gotten from the
2974 : // the return value of ProcessText.
2975 0 : if (mDoMeasureBoundingBox) {
2976 0 : textRunMetrics.mBoundingBox.Scale(1.0 / mAppUnitsPerDevPixel);
2977 0 : mBoundingBox = mBoundingBox.Union(textRunMetrics.mBoundingBox);
2978 : }
2979 :
2980 0 : return NSToCoordRound(textRunMetrics.mAdvanceWidth);
2981 : }
2982 :
2983 0 : virtual void DrawText(nscoord xOffset, nscoord width)
2984 : {
2985 0 : gfxPoint point = mPt;
2986 0 : point.x += xOffset;
2987 :
2988 : // offset is given in terms of left side of string
2989 0 : if (mTextRun->IsRightToLeft()) {
2990 : // Bug 581092 - don't use rounded pixel width to advance to
2991 : // right-hand end of run, because this will cause different
2992 : // glyph positioning for LTR vs RTL drawing of the same
2993 : // glyph string on OS X and DWrite where textrun widths may
2994 : // involve fractional pixels.
2995 : gfxTextRun::Metrics textRunMetrics =
2996 : mTextRun->MeasureText(0,
2997 : mTextRun->GetLength(),
2998 : mDoMeasureBoundingBox ?
2999 : gfxFont::TIGHT_INK_EXTENTS :
3000 : gfxFont::LOOSE_INK_EXTENTS,
3001 : mThebes,
3002 0 : nsnull);
3003 0 : point.x += textRunMetrics.mAdvanceWidth;
3004 : // old code was:
3005 : // point.x += width * mAppUnitsPerDevPixel;
3006 : // TODO: restore this if/when we move to fractional coords
3007 : // throughout the text layout process
3008 : }
3009 :
3010 : PRUint32 numRuns;
3011 0 : const gfxTextRun::GlyphRun *runs = mTextRun->GetGlyphRuns(&numRuns);
3012 0 : const PRUint32 appUnitsPerDevUnit = mAppUnitsPerDevPixel;
3013 0 : const double devUnitsPerAppUnit = 1.0/double(appUnitsPerDevUnit);
3014 : Point baselineOrigin =
3015 0 : Point(point.x * devUnitsPerAppUnit, point.y * devUnitsPerAppUnit);
3016 :
3017 0 : float advanceSum = 0;
3018 :
3019 0 : for (PRUint32 c = 0; c < numRuns; c++) {
3020 0 : gfxFont *font = runs[c].mFont;
3021 0 : PRUint32 endRun = 0;
3022 0 : if (c + 1 < numRuns) {
3023 0 : endRun = runs[c + 1].mCharacterOffset;
3024 : } else {
3025 0 : endRun = mTextRun->GetLength();
3026 : }
3027 :
3028 0 : const gfxTextRun::CompressedGlyph *glyphs = mTextRun->GetCharacterGlyphs();
3029 :
3030 : RefPtr<ScaledFont> scaledFont =
3031 0 : gfxPlatform::GetPlatform()->GetScaledFontForFont(font);
3032 :
3033 0 : if (!scaledFont) {
3034 : // This can occur when something switched DirectWrite off.
3035 : return;
3036 : }
3037 :
3038 : GlyphBuffer buffer;
3039 :
3040 0 : std::vector<Glyph> glyphBuf;
3041 :
3042 0 : for (PRUint32 i = runs[c].mCharacterOffset; i < endRun; i++) {
3043 0 : Glyph newGlyph;
3044 0 : if (glyphs[i].IsSimpleGlyph()) {
3045 0 : newGlyph.mIndex = glyphs[i].GetSimpleGlyph();
3046 0 : if (mTextRun->IsRightToLeft()) {
3047 : newGlyph.mPosition.x = baselineOrigin.x - advanceSum -
3048 0 : glyphs[i].GetSimpleAdvance() * devUnitsPerAppUnit;
3049 : } else {
3050 0 : newGlyph.mPosition.x = baselineOrigin.x + advanceSum;
3051 : }
3052 0 : newGlyph.mPosition.y = baselineOrigin.y;
3053 0 : advanceSum += glyphs[i].GetSimpleAdvance() * devUnitsPerAppUnit;
3054 0 : glyphBuf.push_back(newGlyph);
3055 0 : continue;
3056 : }
3057 :
3058 0 : if (!glyphs[i].GetGlyphCount()) {
3059 0 : continue;
3060 : }
3061 :
3062 : gfxTextRun::DetailedGlyph *detailedGlyphs =
3063 0 : mTextRun->GetDetailedGlyphs(i);
3064 :
3065 0 : for (PRUint32 c = 0; c < glyphs[i].GetGlyphCount(); c++) {
3066 0 : newGlyph.mIndex = detailedGlyphs[c].mGlyphID;
3067 0 : if (mTextRun->IsRightToLeft()) {
3068 0 : newGlyph.mPosition.x = baselineOrigin.x + detailedGlyphs[c].mXOffset * devUnitsPerAppUnit -
3069 0 : advanceSum - detailedGlyphs[c].mAdvance * devUnitsPerAppUnit;
3070 : } else {
3071 0 : newGlyph.mPosition.x = baselineOrigin.x + detailedGlyphs[c].mXOffset * devUnitsPerAppUnit + advanceSum;
3072 : }
3073 0 : newGlyph.mPosition.y = baselineOrigin.y + detailedGlyphs[c].mYOffset * devUnitsPerAppUnit;
3074 0 : glyphBuf.push_back(newGlyph);
3075 0 : advanceSum += detailedGlyphs[c].mAdvance * devUnitsPerAppUnit;
3076 : }
3077 : }
3078 :
3079 0 : if (!glyphBuf.size()) {
3080 : // This may happen for glyph runs for a 0 size font.
3081 0 : continue;
3082 : }
3083 :
3084 0 : buffer.mGlyphs = &glyphBuf.front();
3085 0 : buffer.mNumGlyphs = glyphBuf.size();
3086 :
3087 0 : if (mOp == nsCanvasRenderingContext2DAzure::TEXT_DRAW_OPERATION_FILL) {
3088 0 : nsCanvasRenderingContext2DAzure::AdjustedTarget(mCtx)->
3089 : FillGlyphs(scaledFont, buffer,
3090 0 : nsCanvasRenderingContext2DAzure::GeneralPattern().
3091 0 : ForStyle(mCtx, nsCanvasRenderingContext2DAzure::STYLE_FILL, mCtx->mTarget),
3092 0 : DrawOptions(mState->globalAlpha, mCtx->UsedOperation()));
3093 0 : } else if (mOp == nsCanvasRenderingContext2DAzure::TEXT_DRAW_OPERATION_STROKE) {
3094 0 : RefPtr<Path> path = scaledFont->GetPathForGlyphs(buffer, mCtx->mTarget);
3095 :
3096 0 : const ContextState& state = *mState;
3097 0 : nsCanvasRenderingContext2DAzure::AdjustedTarget(mCtx)->
3098 0 : Stroke(path, nsCanvasRenderingContext2DAzure::GeneralPattern().
3099 0 : ForStyle(mCtx, nsCanvasRenderingContext2DAzure::STYLE_STROKE, mCtx->mTarget),
3100 : StrokeOptions(state.lineWidth, state.lineJoin,
3101 : state.lineCap, state.miterLimit,
3102 : state.dash.Length(),
3103 : state.dash.Elements(),
3104 : state.dashOffset),
3105 0 : DrawOptions(state.globalAlpha, mCtx->UsedOperation()));
3106 :
3107 : }
3108 : }
3109 : }
3110 :
3111 : // current text run
3112 : nsAutoPtr<gfxTextRun> mTextRun;
3113 :
3114 : // pointer to a screen reference context used to measure text and such
3115 : nsRefPtr<gfxContext> mThebes;
3116 :
3117 : // Pointer to the draw target we should fill our text to
3118 : nsCanvasRenderingContext2DAzure *mCtx;
3119 :
3120 : // position of the left side of the string, alphabetic baseline
3121 : gfxPoint mPt;
3122 :
3123 : // current font
3124 : gfxFontGroup* mFontgrp;
3125 :
3126 : // dev pixel conversion factor
3127 : PRUint32 mAppUnitsPerDevPixel;
3128 :
3129 : // operation (fill or stroke)
3130 : nsCanvasRenderingContext2DAzure::TextDrawOperation mOp;
3131 :
3132 : // context state
3133 : ContextState *mState;
3134 :
3135 : // union of bounding boxes of all runs, needed for shadows
3136 : gfxRect mBoundingBox;
3137 :
3138 : // true iff the bounding box should be measured
3139 : bool mDoMeasureBoundingBox;
3140 : };
3141 :
3142 : nsresult
3143 0 : nsCanvasRenderingContext2DAzure::DrawOrMeasureText(const nsAString& aRawText,
3144 : float aX,
3145 : float aY,
3146 : float aMaxWidth,
3147 : TextDrawOperation aOp,
3148 : float* aWidth)
3149 : {
3150 : nsresult rv;
3151 :
3152 0 : if (!FloatValidate(aX, aY, aMaxWidth))
3153 0 : return NS_ERROR_DOM_SYNTAX_ERR;
3154 :
3155 : // spec isn't clear on what should happen if aMaxWidth <= 0, so
3156 : // treat it as an invalid argument
3157 : // technically, 0 should be an invalid value as well, but 0 is the default
3158 : // arg, and there is no way to tell if the default was used
3159 0 : if (aMaxWidth < 0)
3160 0 : return NS_ERROR_INVALID_ARG;
3161 :
3162 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mCanvasElement);
3163 0 : if (!content && !mDocShell) {
3164 0 : NS_WARNING("Canvas element must be an nsIContent and non-null or a docshell must be provided");
3165 0 : return NS_ERROR_FAILURE;
3166 : }
3167 :
3168 0 : nsIPresShell* presShell = GetPresShell();
3169 0 : if (!presShell)
3170 0 : return NS_ERROR_FAILURE;
3171 :
3172 0 : nsIDocument* document = presShell->GetDocument();
3173 :
3174 : // replace all the whitespace characters with U+0020 SPACE
3175 0 : nsAutoString textToDraw(aRawText);
3176 0 : TextReplaceWhitespaceCharacters(textToDraw);
3177 :
3178 : // for now, default to ltr if not in doc
3179 0 : bool isRTL = false;
3180 :
3181 0 : if (content && content->IsInDoc()) {
3182 : // try to find the closest context
3183 : nsRefPtr<nsStyleContext> canvasStyle =
3184 0 : nsComputedDOMStyle::GetStyleContextForElement(content->AsElement(),
3185 : nsnull,
3186 0 : presShell);
3187 0 : if (!canvasStyle) {
3188 0 : return NS_ERROR_FAILURE;
3189 : }
3190 :
3191 0 : isRTL = canvasStyle->GetStyleVisibility()->mDirection ==
3192 0 : NS_STYLE_DIRECTION_RTL;
3193 : } else {
3194 0 : isRTL = GET_BIDI_OPTION_DIRECTION(document->GetBidiOptions()) == IBMBIDI_TEXTDIRECTION_RTL;
3195 : }
3196 :
3197 0 : const ContextState &state = CurrentState();
3198 :
3199 : // This is only needed to know if we can know the drawing bounding box easily.
3200 0 : bool doDrawShadow = aOp == TEXT_DRAW_OPERATION_FILL && NeedToDrawShadow();
3201 :
3202 0 : nsCanvasBidiProcessorAzure processor;
3203 :
3204 0 : GetAppUnitsValues(&processor.mAppUnitsPerDevPixel, nsnull);
3205 0 : processor.mPt = gfxPoint(aX, aY);
3206 : processor.mThebes =
3207 0 : new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface());
3208 0 : Matrix matrix = mTarget->GetTransform();
3209 0 : processor.mThebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21, matrix._22, matrix._31, matrix._32));
3210 0 : processor.mCtx = this;
3211 0 : processor.mOp = aOp;
3212 0 : processor.mBoundingBox = gfxRect(0, 0, 0, 0);
3213 0 : processor.mDoMeasureBoundingBox = doDrawShadow || !mIsEntireFrameInvalid;
3214 0 : processor.mState = &CurrentState();
3215 :
3216 :
3217 0 : processor.mFontgrp = GetCurrentFontStyle();
3218 0 : NS_ASSERTION(processor.mFontgrp, "font group is null");
3219 :
3220 : nscoord totalWidthCoord;
3221 :
3222 : // calls bidi algo twice since it needs the full text width and the
3223 : // bounding boxes before rendering anything
3224 0 : nsBidi bidiEngine;
3225 : rv = nsBidiPresUtils::ProcessText(textToDraw.get(),
3226 0 : textToDraw.Length(),
3227 : isRTL ? NSBIDI_RTL : NSBIDI_LTR,
3228 : presShell->GetPresContext(),
3229 : processor,
3230 : nsBidiPresUtils::MODE_MEASURE,
3231 : nsnull,
3232 : 0,
3233 : &totalWidthCoord,
3234 0 : &bidiEngine);
3235 0 : if (NS_FAILED(rv)) {
3236 0 : return rv;
3237 : }
3238 :
3239 0 : float totalWidth = float(totalWidthCoord) / processor.mAppUnitsPerDevPixel;
3240 0 : if (aWidth) {
3241 0 : *aWidth = totalWidth;
3242 : }
3243 :
3244 : // if only measuring, don't need to do any more work
3245 0 : if (aOp==TEXT_DRAW_OPERATION_MEASURE) {
3246 0 : return NS_OK;
3247 : }
3248 :
3249 : // offset pt.x based on text align
3250 : gfxFloat anchorX;
3251 :
3252 0 : if (state.textAlign == TEXT_ALIGN_CENTER) {
3253 0 : anchorX = .5;
3254 0 : } else if (state.textAlign == TEXT_ALIGN_LEFT ||
3255 0 : (!isRTL && state.textAlign == TEXT_ALIGN_START) ||
3256 : (isRTL && state.textAlign == TEXT_ALIGN_END)) {
3257 0 : anchorX = 0;
3258 : } else {
3259 0 : anchorX = 1;
3260 : }
3261 :
3262 0 : processor.mPt.x -= anchorX * totalWidth;
3263 :
3264 : // offset pt.y based on text baseline
3265 0 : NS_ASSERTION(processor.mFontgrp->FontListLength()>0, "font group contains no fonts");
3266 0 : const gfxFont::Metrics& fontMetrics = processor.mFontgrp->GetFontAt(0)->GetMetrics();
3267 :
3268 : gfxFloat anchorY;
3269 :
3270 0 : switch (state.textBaseline)
3271 : {
3272 : case TEXT_BASELINE_HANGING:
3273 : // fall through; best we can do with the information available
3274 : case TEXT_BASELINE_TOP:
3275 0 : anchorY = fontMetrics.emAscent;
3276 0 : break;
3277 : break;
3278 : case TEXT_BASELINE_MIDDLE:
3279 0 : anchorY = (fontMetrics.emAscent - fontMetrics.emDescent) * .5f;
3280 0 : break;
3281 : case TEXT_BASELINE_IDEOGRAPHIC:
3282 : // fall through; best we can do with the information available
3283 : case TEXT_BASELINE_ALPHABETIC:
3284 0 : anchorY = 0;
3285 0 : break;
3286 : case TEXT_BASELINE_BOTTOM:
3287 0 : anchorY = -fontMetrics.emDescent;
3288 0 : break;
3289 : }
3290 :
3291 0 : processor.mPt.y += anchorY;
3292 :
3293 : // correct bounding box to get it to be the correct size/position
3294 0 : processor.mBoundingBox.width = totalWidth;
3295 0 : processor.mBoundingBox.MoveBy(processor.mPt);
3296 :
3297 0 : processor.mPt.x *= processor.mAppUnitsPerDevPixel;
3298 0 : processor.mPt.y *= processor.mAppUnitsPerDevPixel;
3299 :
3300 0 : Matrix oldTransform = mTarget->GetTransform();
3301 : // if text is over aMaxWidth, then scale the text horizontally such that its
3302 : // width is precisely aMaxWidth
3303 0 : if (aMaxWidth > 0 && totalWidth > aMaxWidth) {
3304 0 : Matrix newTransform = oldTransform;
3305 :
3306 : // Translate so that the anchor point is at 0,0, then scale and then
3307 : // translate back.
3308 0 : newTransform.Translate(aX, 0);
3309 0 : newTransform.Scale(aMaxWidth / totalWidth, 1);
3310 0 : newTransform.Translate(-aX, 0);
3311 : /* we do this to avoid an ICE in the android compiler */
3312 0 : Matrix androidCompilerBug = newTransform;
3313 0 : mTarget->SetTransform(androidCompilerBug);
3314 : }
3315 :
3316 : // save the previous bounding box
3317 0 : gfxRect boundingBox = processor.mBoundingBox;
3318 :
3319 : // don't ever need to measure the bounding box twice
3320 0 : processor.mDoMeasureBoundingBox = false;
3321 :
3322 : rv = nsBidiPresUtils::ProcessText(textToDraw.get(),
3323 0 : textToDraw.Length(),
3324 : isRTL ? NSBIDI_RTL : NSBIDI_LTR,
3325 : presShell->GetPresContext(),
3326 : processor,
3327 : nsBidiPresUtils::MODE_DRAW,
3328 : nsnull,
3329 : 0,
3330 : nsnull,
3331 0 : &bidiEngine);
3332 :
3333 :
3334 0 : mTarget->SetTransform(oldTransform);
3335 :
3336 0 : if (aOp == nsCanvasRenderingContext2DAzure::TEXT_DRAW_OPERATION_FILL && !doDrawShadow)
3337 0 : return RedrawUser(boundingBox);
3338 :
3339 0 : return Redraw();
3340 : }
3341 :
3342 : NS_IMETHODIMP
3343 0 : nsCanvasRenderingContext2DAzure::SetMozTextStyle(const nsAString& textStyle)
3344 : {
3345 : // font and mozTextStyle are the same value
3346 0 : return SetFont(textStyle);
3347 : }
3348 :
3349 : NS_IMETHODIMP
3350 0 : nsCanvasRenderingContext2DAzure::GetMozTextStyle(nsAString& textStyle)
3351 : {
3352 : // font and mozTextStyle are the same value
3353 0 : return GetFont(textStyle);
3354 : }
3355 :
3356 0 : gfxFontGroup *nsCanvasRenderingContext2DAzure::GetCurrentFontStyle()
3357 : {
3358 : // use lazy initilization for the font group since it's rather expensive
3359 0 : if (!CurrentState().fontGroup) {
3360 0 : nsresult rv = SetFont(kDefaultFontStyle);
3361 0 : if (NS_FAILED(rv)) {
3362 0 : gfxFontStyle style;
3363 0 : style.size = kDefaultFontSize;
3364 0 : CurrentState().fontGroup =
3365 0 : gfxPlatform::GetPlatform()->CreateFontGroup(kDefaultFontName,
3366 : &style,
3367 0 : nsnull);
3368 0 : if (CurrentState().fontGroup) {
3369 0 : CurrentState().font = kDefaultFontStyle;
3370 0 : rv = NS_OK;
3371 : } else {
3372 0 : rv = NS_ERROR_OUT_OF_MEMORY;
3373 : }
3374 : }
3375 :
3376 0 : NS_ASSERTION(NS_SUCCEEDED(rv), "Default canvas font is invalid");
3377 : }
3378 :
3379 0 : return CurrentState().fontGroup;
3380 : }
3381 :
3382 : //
3383 : // line caps/joins
3384 : //
3385 : NS_IMETHODIMP
3386 0 : nsCanvasRenderingContext2DAzure::SetLineWidth(float width)
3387 : {
3388 0 : if (!FloatValidate(width) || width <= 0.0) {
3389 0 : return NS_OK;
3390 : }
3391 :
3392 0 : CurrentState().lineWidth = width;
3393 0 : return NS_OK;
3394 : }
3395 :
3396 : NS_IMETHODIMP
3397 0 : nsCanvasRenderingContext2DAzure::GetLineWidth(float *width)
3398 : {
3399 0 : *width = CurrentState().lineWidth;
3400 0 : return NS_OK;
3401 : }
3402 :
3403 : NS_IMETHODIMP
3404 0 : nsCanvasRenderingContext2DAzure::SetLineCap(const nsAString& capstyle)
3405 : {
3406 : CapStyle cap;
3407 :
3408 0 : if (capstyle.EqualsLiteral("butt")) {
3409 0 : cap = CAP_BUTT;
3410 0 : } else if (capstyle.EqualsLiteral("round")) {
3411 0 : cap = CAP_ROUND;
3412 0 : } else if (capstyle.EqualsLiteral("square")) {
3413 0 : cap = CAP_SQUARE;
3414 : } else {
3415 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3416 0 : return NS_OK;
3417 : }
3418 :
3419 0 : CurrentState().lineCap = cap;
3420 :
3421 0 : return NS_OK;
3422 : }
3423 :
3424 : NS_IMETHODIMP
3425 0 : nsCanvasRenderingContext2DAzure::GetLineCap(nsAString& capstyle)
3426 : {
3427 0 : switch (CurrentState().lineCap) {
3428 : case CAP_BUTT:
3429 0 : capstyle.AssignLiteral("butt");
3430 0 : break;
3431 : case CAP_ROUND:
3432 0 : capstyle.AssignLiteral("round");
3433 0 : break;
3434 : case CAP_SQUARE:
3435 0 : capstyle.AssignLiteral("square");
3436 0 : break;
3437 : }
3438 :
3439 0 : return NS_OK;
3440 : }
3441 :
3442 : NS_IMETHODIMP
3443 0 : nsCanvasRenderingContext2DAzure::SetLineJoin(const nsAString& joinstyle)
3444 : {
3445 : JoinStyle j;
3446 :
3447 0 : if (joinstyle.EqualsLiteral("round")) {
3448 0 : j = JOIN_ROUND;
3449 0 : } else if (joinstyle.EqualsLiteral("bevel")) {
3450 0 : j = JOIN_BEVEL;
3451 0 : } else if (joinstyle.EqualsLiteral("miter")) {
3452 0 : j = JOIN_MITER_OR_BEVEL;
3453 : } else {
3454 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3455 0 : return NS_OK;
3456 : }
3457 :
3458 0 : CurrentState().lineJoin = j;
3459 :
3460 0 : return NS_OK;
3461 : }
3462 :
3463 : NS_IMETHODIMP
3464 0 : nsCanvasRenderingContext2DAzure::GetLineJoin(nsAString& joinstyle)
3465 : {
3466 0 : switch (CurrentState().lineJoin) {
3467 : case JOIN_ROUND:
3468 0 : joinstyle.AssignLiteral("round");
3469 0 : break;
3470 : case JOIN_BEVEL:
3471 0 : joinstyle.AssignLiteral("bevel");
3472 0 : break;
3473 : case JOIN_MITER_OR_BEVEL:
3474 0 : joinstyle.AssignLiteral("miter");
3475 0 : break;
3476 : default:
3477 0 : return NS_ERROR_FAILURE;
3478 : }
3479 :
3480 0 : return NS_OK;
3481 : }
3482 :
3483 : NS_IMETHODIMP
3484 0 : nsCanvasRenderingContext2DAzure::SetMiterLimit(float miter)
3485 : {
3486 0 : if (!FloatValidate(miter) || miter <= 0.0)
3487 0 : return NS_OK;
3488 :
3489 0 : CurrentState().miterLimit = miter;
3490 :
3491 0 : return NS_OK;
3492 : }
3493 :
3494 : NS_IMETHODIMP
3495 0 : nsCanvasRenderingContext2DAzure::GetMiterLimit(float *miter)
3496 : {
3497 0 : *miter = CurrentState().miterLimit;
3498 0 : return NS_OK;
3499 : }
3500 :
3501 : NS_IMETHODIMP
3502 0 : nsCanvasRenderingContext2DAzure::SetMozDash(JSContext *cx, const jsval& patternArray)
3503 : {
3504 0 : FallibleTArray<Float> dash;
3505 0 : nsresult rv = JSValToDashArray(cx, patternArray, dash);
3506 0 : if (NS_SUCCEEDED(rv)) {
3507 0 : ContextState& state = CurrentState();
3508 0 : state.dash = dash;
3509 0 : if (state.dash.IsEmpty()) {
3510 0 : state.dashOffset = 0;
3511 : }
3512 : }
3513 0 : return rv;
3514 : }
3515 :
3516 : NS_IMETHODIMP
3517 0 : nsCanvasRenderingContext2DAzure::GetMozDash(JSContext* cx, jsval* dashArray)
3518 : {
3519 0 : return DashArrayToJSVal(CurrentState().dash, cx, dashArray);
3520 : }
3521 :
3522 : NS_IMETHODIMP
3523 0 : nsCanvasRenderingContext2DAzure::SetMozDashOffset(float offset)
3524 : {
3525 0 : if (!FloatValidate(offset)) {
3526 0 : return NS_ERROR_ILLEGAL_VALUE;
3527 : }
3528 0 : ContextState& state = CurrentState();
3529 0 : if (!state.dash.IsEmpty()) {
3530 0 : state.dashOffset = offset;
3531 : }
3532 0 : return NS_OK;
3533 : }
3534 :
3535 : NS_IMETHODIMP
3536 0 : nsCanvasRenderingContext2DAzure::GetMozDashOffset(float* offset)
3537 : {
3538 0 : *offset = CurrentState().dashOffset;
3539 0 : return NS_OK;
3540 : }
3541 :
3542 : NS_IMETHODIMP
3543 0 : nsCanvasRenderingContext2DAzure::IsPointInPath(float x, float y, bool *retVal)
3544 : {
3545 0 : if (!FloatValidate(x,y)) {
3546 0 : *retVal = false;
3547 0 : return NS_OK;
3548 : }
3549 :
3550 0 : EnsureUserSpacePath();
3551 :
3552 0 : *retVal = false;
3553 :
3554 0 : if (mPath) {
3555 0 : *retVal = mPath->ContainsPoint(Point(x, y), mTarget->GetTransform());
3556 : }
3557 :
3558 0 : return NS_OK;
3559 : }
3560 :
3561 : //
3562 : // image
3563 : //
3564 :
3565 : // drawImage(in HTMLImageElement image, in float dx, in float dy);
3566 : // -- render image from 0,0 at dx,dy top-left coords
3567 : // drawImage(in HTMLImageElement image, in float dx, in float dy, in float sw, in float sh);
3568 : // -- render image from 0,0 at dx,dy top-left coords clipping it to sw,sh
3569 : // 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);
3570 : // -- render the region defined by (sx,sy,sw,wh) in image-local space into the region (dx,dy,dw,dh) on the canvas
3571 :
3572 : NS_IMETHODIMP
3573 0 : nsCanvasRenderingContext2DAzure::DrawImage(nsIDOMElement *imgElt, float a1,
3574 : float a2, float a3, float a4, float a5,
3575 : float a6, float a7, float a8,
3576 : PRUint8 optional_argc)
3577 : {
3578 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(imgElt);
3579 0 : if (!content) {
3580 0 : return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
3581 : }
3582 :
3583 0 : if (optional_argc == 0) {
3584 0 : if (!FloatValidate(a1, a2)) {
3585 0 : return NS_OK;
3586 : }
3587 0 : } else if (optional_argc == 2) {
3588 0 : if (!FloatValidate(a1, a2, a3, a4)) {
3589 0 : return NS_OK;
3590 : }
3591 0 : } else if (optional_argc == 6) {
3592 0 : if (!FloatValidate(a1, a2, a3, a4, a5, a6) || !FloatValidate(a7, a8)) {
3593 0 : return NS_OK;
3594 : }
3595 : }
3596 :
3597 : double sx,sy,sw,sh;
3598 : double dx,dy,dw,dh;
3599 :
3600 0 : RefPtr<SourceSurface> srcSurf;
3601 0 : gfxIntSize imgSize;
3602 :
3603 0 : nsHTMLCanvasElement* canvas = nsHTMLCanvasElement::FromContent(content);
3604 0 : if (canvas) {
3605 0 : nsIntSize size = canvas->GetSize();
3606 0 : if (size.width == 0 || size.height == 0) {
3607 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
3608 : }
3609 :
3610 : // Special case for Canvas, which could be an Azure canvas!
3611 0 : nsICanvasRenderingContextInternal *srcCanvas = canvas->GetContextAtIndex(0);
3612 0 : if (srcCanvas == this) {
3613 : // Self-copy.
3614 0 : srcSurf = mTarget->Snapshot();
3615 0 : imgSize = gfxIntSize(mWidth, mHeight);
3616 0 : } else if (srcCanvas) {
3617 : // This might not be an Azure canvas!
3618 0 : srcSurf = srcCanvas->GetSurfaceSnapshot();
3619 :
3620 0 : if (srcSurf && mCanvasElement) {
3621 : // Do security check here.
3622 : CanvasUtils::DoDrawImageSecurityCheck(HTMLCanvasElement(),
3623 0 : content->NodePrincipal(), canvas->IsWriteOnly(),
3624 0 : false);
3625 0 : imgSize = gfxIntSize(srcSurf->GetSize().width, srcSurf->GetSize().height);
3626 : }
3627 : }
3628 : } else {
3629 : gfxASurface* imgsurf =
3630 0 : CanvasImageCache::Lookup(imgElt, HTMLCanvasElement(), &imgSize);
3631 0 : if (imgsurf) {
3632 0 : srcSurf = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mTarget, imgsurf);
3633 : }
3634 : }
3635 :
3636 0 : if (!srcSurf) {
3637 : // The canvas spec says that drawImage should draw the first frame
3638 : // of animated images
3639 0 : PRUint32 sfeFlags = nsLayoutUtils::SFE_WANT_FIRST_FRAME;
3640 : nsLayoutUtils::SurfaceFromElementResult res =
3641 0 : nsLayoutUtils::SurfaceFromElement(content->AsElement(), sfeFlags);
3642 :
3643 0 : if (!res.mSurface) {
3644 : // Spec says to silently do nothing if the element is still loading.
3645 0 : return res.mIsStillLoading ? NS_OK : NS_ERROR_NOT_AVAILABLE;
3646 : }
3647 :
3648 : // Ignore cairo surfaces that are bad! See bug 666312.
3649 0 : if (res.mSurface->CairoStatus()) {
3650 0 : return NS_OK;
3651 : }
3652 :
3653 0 : imgSize = res.mSize;
3654 :
3655 0 : if (mCanvasElement) {
3656 : CanvasUtils::DoDrawImageSecurityCheck(HTMLCanvasElement(),
3657 : res.mPrincipal, res.mIsWriteOnly,
3658 0 : res.mCORSUsed);
3659 : }
3660 :
3661 0 : if (res.mImageRequest) {
3662 : CanvasImageCache::NotifyDrawImage(imgElt, HTMLCanvasElement(),
3663 0 : res.mImageRequest, res.mSurface, imgSize);
3664 : }
3665 :
3666 0 : srcSurf = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mTarget, res.mSurface);
3667 : }
3668 :
3669 0 : if (optional_argc == 0) {
3670 0 : dx = a1;
3671 0 : dy = a2;
3672 0 : sx = sy = 0.0;
3673 0 : dw = sw = (double) imgSize.width;
3674 0 : dh = sh = (double) imgSize.height;
3675 0 : } else if (optional_argc == 2) {
3676 0 : dx = a1;
3677 0 : dy = a2;
3678 0 : dw = a3;
3679 0 : dh = a4;
3680 0 : sx = sy = 0.0;
3681 0 : sw = (double) imgSize.width;
3682 0 : sh = (double) imgSize.height;
3683 0 : } else if (optional_argc == 6) {
3684 0 : sx = a1;
3685 0 : sy = a2;
3686 0 : sw = a3;
3687 0 : sh = a4;
3688 0 : dx = a5;
3689 0 : dy = a6;
3690 0 : dw = a7;
3691 0 : dh = a8;
3692 : } else {
3693 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3694 0 : return NS_ERROR_INVALID_ARG;
3695 : }
3696 :
3697 0 : if (dw == 0.0 || dh == 0.0) {
3698 : // not really failure, but nothing to do --
3699 : // and noone likes a divide-by-zero
3700 0 : return NS_OK;
3701 : }
3702 :
3703 0 : if (sx < 0.0 || sy < 0.0 ||
3704 : sw < 0.0 || sw > (double) imgSize.width ||
3705 : sh < 0.0 || sh > (double) imgSize.height ||
3706 : dw < 0.0 || dh < 0.0) {
3707 : // XXX - Unresolved spec issues here, for now return error.
3708 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
3709 : }
3710 :
3711 : Filter filter;
3712 :
3713 0 : if (CurrentState().imageSmoothingEnabled)
3714 0 : filter = mgfx::FILTER_LINEAR;
3715 : else
3716 0 : filter = mgfx::FILTER_POINT;
3717 :
3718 0 : mgfx::Rect bounds;
3719 :
3720 0 : if (NeedToDrawShadow()) {
3721 0 : bounds = mgfx::Rect(dx, dy, dw, dh);
3722 0 : bounds = mTarget->GetTransform().TransformBounds(bounds);
3723 : }
3724 :
3725 0 : AdjustedTarget(this, bounds.IsEmpty() ? nsnull : &bounds)->
3726 : DrawSurface(srcSurf,
3727 : mgfx::Rect(dx, dy, dw, dh),
3728 : mgfx::Rect(sx, sy, sw, sh),
3729 : DrawSurfaceOptions(filter),
3730 0 : DrawOptions(CurrentState().globalAlpha, UsedOperation()));
3731 :
3732 0 : return RedrawUser(gfxRect(dx, dy, dw, dh));
3733 : }
3734 :
3735 : NS_IMETHODIMP
3736 0 : nsCanvasRenderingContext2DAzure::SetGlobalCompositeOperation(const nsAString& op)
3737 : {
3738 : CompositionOp comp_op;
3739 :
3740 : #define CANVAS_OP_TO_GFX_OP(cvsop, op2d) \
3741 : if (op.EqualsLiteral(cvsop)) \
3742 : comp_op = OP_##op2d;
3743 :
3744 0 : CANVAS_OP_TO_GFX_OP("copy", SOURCE)
3745 0 : else CANVAS_OP_TO_GFX_OP("source-atop", ATOP)
3746 0 : else CANVAS_OP_TO_GFX_OP("source-in", IN)
3747 0 : else CANVAS_OP_TO_GFX_OP("source-out", OUT)
3748 0 : else CANVAS_OP_TO_GFX_OP("source-over", OVER)
3749 0 : else CANVAS_OP_TO_GFX_OP("destination-in", DEST_IN)
3750 0 : else CANVAS_OP_TO_GFX_OP("destination-out", DEST_OUT)
3751 0 : else CANVAS_OP_TO_GFX_OP("destination-over", DEST_OVER)
3752 0 : else CANVAS_OP_TO_GFX_OP("destination-atop", DEST_ATOP)
3753 0 : else CANVAS_OP_TO_GFX_OP("lighter", ADD)
3754 0 : else CANVAS_OP_TO_GFX_OP("xor", XOR)
3755 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3756 0 : else return NS_OK;
3757 :
3758 : #undef CANVAS_OP_TO_GFX_OP
3759 0 : CurrentState().op = comp_op;
3760 0 : return NS_OK;
3761 : }
3762 :
3763 : NS_IMETHODIMP
3764 0 : nsCanvasRenderingContext2DAzure::GetGlobalCompositeOperation(nsAString& op)
3765 : {
3766 0 : CompositionOp comp_op = CurrentState().op;
3767 :
3768 : #define CANVAS_OP_TO_GFX_OP(cvsop, op2d) \
3769 : if (comp_op == OP_##op2d) \
3770 : op.AssignLiteral(cvsop);
3771 :
3772 0 : CANVAS_OP_TO_GFX_OP("copy", SOURCE)
3773 0 : else CANVAS_OP_TO_GFX_OP("destination-atop", DEST_ATOP)
3774 0 : else CANVAS_OP_TO_GFX_OP("destination-in", DEST_IN)
3775 0 : else CANVAS_OP_TO_GFX_OP("destination-out", DEST_OUT)
3776 0 : else CANVAS_OP_TO_GFX_OP("destination-over", DEST_OVER)
3777 0 : else CANVAS_OP_TO_GFX_OP("lighter", ADD)
3778 0 : else CANVAS_OP_TO_GFX_OP("source-atop", ATOP)
3779 0 : else CANVAS_OP_TO_GFX_OP("source-in", IN)
3780 0 : else CANVAS_OP_TO_GFX_OP("source-out", OUT)
3781 0 : else CANVAS_OP_TO_GFX_OP("source-over", OVER)
3782 0 : else CANVAS_OP_TO_GFX_OP("xor", XOR)
3783 0 : else return NS_ERROR_FAILURE;
3784 :
3785 : #undef CANVAS_OP_TO_GFX_OP
3786 :
3787 0 : return NS_OK;
3788 : }
3789 :
3790 : NS_IMETHODIMP
3791 0 : nsCanvasRenderingContext2DAzure::DrawWindow(nsIDOMWindow* aWindow, float aX, float aY,
3792 : float aW, float aH,
3793 : const nsAString& aBGColor,
3794 : PRUint32 flags)
3795 : {
3796 0 : NS_ENSURE_ARG(aWindow != nsnull);
3797 :
3798 : // protect against too-large surfaces that will cause allocation
3799 : // or overflow issues
3800 0 : if (!gfxASurface::CheckSurfaceSize(gfxIntSize(PRInt32(aW), PRInt32(aH)),
3801 0 : 0xffff))
3802 0 : return NS_ERROR_FAILURE;
3803 :
3804 0 : nsRefPtr<gfxASurface> drawSurf;
3805 0 : GetThebesSurface(getter_AddRefs(drawSurf));
3806 :
3807 0 : nsRefPtr<gfxContext> thebes = new gfxContext(drawSurf);
3808 :
3809 0 : Matrix matrix = mTarget->GetTransform();
3810 : thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21,
3811 0 : matrix._22, matrix._31, matrix._32));
3812 :
3813 : // We can't allow web apps to call this until we fix at least the
3814 : // following potential security issues:
3815 : // -- rendering cross-domain IFRAMEs and then extracting the results
3816 : // -- rendering the user's theme and then extracting the results
3817 : // -- rendering native anonymous content (e.g., file input paths;
3818 : // scrollbars should be allowed)
3819 0 : if (!nsContentUtils::IsCallerTrustedForRead()) {
3820 : // not permitted to use DrawWindow
3821 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3822 0 : return NS_ERROR_DOM_SECURITY_ERR;
3823 : }
3824 :
3825 : // Flush layout updates
3826 0 : if (!(flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH))
3827 0 : nsContentUtils::FlushLayoutForTree(aWindow);
3828 :
3829 0 : nsRefPtr<nsPresContext> presContext;
3830 0 : nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aWindow);
3831 0 : if (win) {
3832 0 : nsIDocShell* docshell = win->GetDocShell();
3833 0 : if (docshell) {
3834 0 : docshell->GetPresContext(getter_AddRefs(presContext));
3835 : }
3836 : }
3837 0 : if (!presContext)
3838 0 : return NS_ERROR_FAILURE;
3839 :
3840 : nscolor bgColor;
3841 :
3842 : nsIDocument* elementDoc = mCanvasElement ?
3843 0 : HTMLCanvasElement()->OwnerDoc() : nsnull;
3844 :
3845 : // Pass the CSS Loader object to the parser, to allow parser error reports
3846 : // to include the outer window ID.
3847 0 : nsCSSParser parser(elementDoc ? elementDoc->CSSLoader() : nsnull);
3848 0 : nsresult rv = parser.ParseColorString(PromiseFlatString(aBGColor),
3849 0 : nsnull, 0, &bgColor);
3850 0 : NS_ENSURE_SUCCESS(rv, rv);
3851 :
3852 0 : nsIPresShell* presShell = presContext->PresShell();
3853 0 : NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
3854 :
3855 : nsRect r(nsPresContext::CSSPixelsToAppUnits(aX),
3856 : nsPresContext::CSSPixelsToAppUnits(aY),
3857 : nsPresContext::CSSPixelsToAppUnits(aW),
3858 0 : nsPresContext::CSSPixelsToAppUnits(aH));
3859 : PRUint32 renderDocFlags = (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
3860 0 : nsIPresShell::RENDER_DOCUMENT_RELATIVE);
3861 0 : if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_CARET) {
3862 0 : renderDocFlags |= nsIPresShell::RENDER_CARET;
3863 : }
3864 0 : if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_VIEW) {
3865 : renderDocFlags &= ~(nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
3866 0 : nsIPresShell::RENDER_DOCUMENT_RELATIVE);
3867 : }
3868 0 : if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_USE_WIDGET_LAYERS) {
3869 0 : renderDocFlags |= nsIPresShell::RENDER_USE_WIDGET_LAYERS;
3870 : }
3871 0 : if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_ASYNC_DECODE_IMAGES) {
3872 0 : renderDocFlags |= nsIPresShell::RENDER_ASYNC_DECODE_IMAGES;
3873 : }
3874 :
3875 0 : rv = presShell->RenderDocument(r, renderDocFlags, bgColor, thebes);
3876 :
3877 : // note that aX and aY are coordinates in the document that
3878 : // we're drawing; aX and aY are drawn to 0,0 in current user
3879 : // space.
3880 0 : RedrawUser(gfxRect(0, 0, aW, aH));
3881 :
3882 0 : return NS_OK;
3883 : }
3884 :
3885 : NS_IMETHODIMP
3886 0 : nsCanvasRenderingContext2DAzure::AsyncDrawXULElement(nsIDOMXULElement* aElem,
3887 : float aX, float aY,
3888 : float aW, float aH,
3889 : const nsAString& aBGColor,
3890 : PRUint32 flags)
3891 : {
3892 0 : NS_ENSURE_ARG(aElem != nsnull);
3893 :
3894 : // We can't allow web apps to call this until we fix at least the
3895 : // following potential security issues:
3896 : // -- rendering cross-domain IFRAMEs and then extracting the results
3897 : // -- rendering the user's theme and then extracting the results
3898 : // -- rendering native anonymous content (e.g., file input paths;
3899 : // scrollbars should be allowed)
3900 0 : if (!nsContentUtils::IsCallerTrustedForRead()) {
3901 : // not permitted to use DrawWindow
3902 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3903 0 : return NS_ERROR_DOM_SECURITY_ERR;
3904 : }
3905 :
3906 : #if 0
3907 : nsCOMPtr<nsIFrameLoaderOwner> loaderOwner = do_QueryInterface(aElem);
3908 : if (!loaderOwner)
3909 : return NS_ERROR_FAILURE;
3910 :
3911 : nsRefPtr<nsFrameLoader> frameloader = loaderOwner->GetFrameLoader();
3912 : if (!frameloader)
3913 : return NS_ERROR_FAILURE;
3914 :
3915 : PBrowserParent *child = frameloader->GetRemoteBrowser();
3916 : if (!child) {
3917 : nsCOMPtr<nsIDOMWindow> window =
3918 : do_GetInterface(frameloader->GetExistingDocShell());
3919 : if (!window)
3920 : return NS_ERROR_FAILURE;
3921 :
3922 : return DrawWindow(window, aX, aY, aW, aH, aBGColor, flags);
3923 : }
3924 :
3925 : // protect against too-large surfaces that will cause allocation
3926 : // or overflow issues
3927 : if (!gfxASurface::CheckSurfaceSize(gfxIntSize(aW, aH), 0xffff))
3928 : return NS_ERROR_FAILURE;
3929 :
3930 : bool flush =
3931 : (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH) == 0;
3932 :
3933 : PRUint32 renderDocFlags = nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
3934 : if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_CARET) {
3935 : renderDocFlags |= nsIPresShell::RENDER_CARET;
3936 : }
3937 : if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_VIEW) {
3938 : renderDocFlags &= ~nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
3939 : }
3940 :
3941 : nsRect rect(nsPresContext::CSSPixelsToAppUnits(aX),
3942 : nsPresContext::CSSPixelsToAppUnits(aY),
3943 : nsPresContext::CSSPixelsToAppUnits(aW),
3944 : nsPresContext::CSSPixelsToAppUnits(aH));
3945 : if (mIPC) {
3946 : PDocumentRendererParent *pdocrender =
3947 : child->SendPDocumentRendererConstructor(rect,
3948 : mThebes->CurrentMatrix(),
3949 : nsString(aBGColor),
3950 : renderDocFlags, flush,
3951 : nsIntSize(mWidth, mHeight));
3952 : if (!pdocrender)
3953 : return NS_ERROR_FAILURE;
3954 :
3955 : DocumentRendererParent *docrender =
3956 : static_cast<DocumentRendererParent *>(pdocrender);
3957 :
3958 : docrender->SetCanvasContext(this, mThebes);
3959 : }
3960 : #endif
3961 0 : return NS_OK;
3962 : }
3963 :
3964 : //
3965 : // device pixel getting/setting
3966 : //
3967 :
3968 : void
3969 0 : nsCanvasRenderingContext2DAzure::EnsureUnpremultiplyTable() {
3970 0 : if (sUnpremultiplyTable)
3971 0 : return;
3972 :
3973 : // Infallably alloc the unpremultiply table.
3974 0 : sUnpremultiplyTable = new PRUint8[256][256];
3975 :
3976 : // It's important that the array be indexed first by alpha and then by rgb
3977 : // value. When we unpremultiply a pixel, we're guaranteed to do three
3978 : // lookups with the same alpha; indexing by alpha first makes it likely that
3979 : // those three lookups will be close to one another in memory, thus
3980 : // increasing the chance of a cache hit.
3981 :
3982 : // a == 0 case
3983 0 : for (PRUint32 c = 0; c <= 255; c++) {
3984 0 : sUnpremultiplyTable[0][c] = c;
3985 : }
3986 :
3987 0 : for (int a = 1; a <= 255; a++) {
3988 0 : for (int c = 0; c <= 255; c++) {
3989 0 : sUnpremultiplyTable[a][c] = (PRUint8)((c * 255) / a);
3990 : }
3991 : }
3992 : }
3993 :
3994 :
3995 : NS_IMETHODIMP
3996 0 : nsCanvasRenderingContext2DAzure::GetImageData()
3997 : {
3998 : /* Should never be called -- GetImageData_explicit is the QS entry point */
3999 0 : return NS_ERROR_NOT_IMPLEMENTED;
4000 : }
4001 :
4002 : NS_IMETHODIMP
4003 0 : nsCanvasRenderingContext2DAzure::GetImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 w, PRUint32 h,
4004 : PRUint8 *aData, PRUint32 aDataLen)
4005 : {
4006 0 : if (!mValid)
4007 0 : return NS_ERROR_FAILURE;
4008 :
4009 0 : if (!mCanvasElement && !mDocShell) {
4010 0 : NS_ERROR("No canvas element and no docshell in GetImageData!!!");
4011 0 : return NS_ERROR_DOM_SECURITY_ERR;
4012 : }
4013 :
4014 : // Check only if we have a canvas element; if we were created with a docshell,
4015 : // then it's special internal use.
4016 0 : if (mCanvasElement &&
4017 0 : HTMLCanvasElement()->IsWriteOnly() &&
4018 0 : !nsContentUtils::IsCallerTrustedForRead())
4019 : {
4020 : // XXX ERRMSG we need to report an error to developers here! (bug 329026)
4021 0 : return NS_ERROR_DOM_SECURITY_ERR;
4022 : }
4023 :
4024 0 : if (w == 0 || h == 0 || aDataLen != w * h * 4)
4025 0 : return NS_ERROR_DOM_SYNTAX_ERR;
4026 :
4027 0 : CheckedInt32 rightMost = CheckedInt32(x) + w;
4028 0 : CheckedInt32 bottomMost = CheckedInt32(y) + h;
4029 :
4030 0 : if (!rightMost.valid() || !bottomMost.valid())
4031 0 : return NS_ERROR_DOM_SYNTAX_ERR;
4032 :
4033 0 : if (mZero) {
4034 0 : return NS_OK;
4035 : }
4036 :
4037 0 : IntRect srcRect(0, 0, mWidth, mHeight);
4038 0 : IntRect destRect(x, y, w, h);
4039 :
4040 0 : if (!srcRect.Contains(destRect)) {
4041 : // Some data is outside the canvas surface, clear the destination.
4042 0 : memset(aData, 0, aDataLen);
4043 : }
4044 :
4045 0 : IntRect srcReadRect = srcRect.Intersect(destRect);
4046 0 : IntRect dstWriteRect = srcReadRect;
4047 0 : dstWriteRect.MoveBy(-x, -y);
4048 :
4049 0 : PRUint8 *src = aData;
4050 0 : PRUint32 srcStride = w * 4;
4051 :
4052 0 : RefPtr<DataSourceSurface> readback;
4053 0 : if (!srcReadRect.IsEmpty()) {
4054 0 : RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
4055 :
4056 0 : readback = snapshot->GetDataSurface();
4057 :
4058 0 : srcStride = readback->Stride();
4059 0 : src = readback->GetData() + srcReadRect.y * srcStride + srcReadRect.x * 4;
4060 : }
4061 :
4062 : // make sure sUnpremultiplyTable has been created
4063 0 : EnsureUnpremultiplyTable();
4064 :
4065 : // NOTE! dst is the same as src, and this relies on reading
4066 : // from src and advancing that ptr before writing to dst.
4067 0 : PRUint8 *dst = aData + dstWriteRect.y * (w * 4) + dstWriteRect.x * 4;
4068 :
4069 0 : for (int j = 0; j < dstWriteRect.height; j++) {
4070 0 : for (int i = 0; i < dstWriteRect.width; i++) {
4071 : // XXX Is there some useful swizzle MMX we can use here?
4072 : #ifdef IS_LITTLE_ENDIAN
4073 0 : PRUint8 b = *src++;
4074 0 : PRUint8 g = *src++;
4075 0 : PRUint8 r = *src++;
4076 0 : PRUint8 a = *src++;
4077 : #else
4078 : PRUint8 a = *src++;
4079 : PRUint8 r = *src++;
4080 : PRUint8 g = *src++;
4081 : PRUint8 b = *src++;
4082 : #endif
4083 : // Convert to non-premultiplied color
4084 0 : *dst++ = sUnpremultiplyTable[a][r];
4085 0 : *dst++ = sUnpremultiplyTable[a][g];
4086 0 : *dst++ = sUnpremultiplyTable[a][b];
4087 0 : *dst++ = a;
4088 : }
4089 0 : src += srcStride - (dstWriteRect.width * 4);
4090 0 : dst += (w * 4) - (dstWriteRect.width * 4);
4091 : }
4092 0 : return NS_OK;
4093 : }
4094 :
4095 : void
4096 0 : nsCanvasRenderingContext2DAzure::EnsurePremultiplyTable() {
4097 0 : if (sPremultiplyTable)
4098 0 : return;
4099 :
4100 : // Infallably alloc the premultiply table.
4101 0 : sPremultiplyTable = new PRUint8[256][256];
4102 :
4103 : // Like the unpremultiply table, it's important that we index the premultiply
4104 : // table with the alpha value as the first index to ensure good cache
4105 : // performance.
4106 :
4107 0 : for (int a = 0; a <= 255; a++) {
4108 0 : for (int c = 0; c <= 255; c++) {
4109 0 : sPremultiplyTable[a][c] = (a * c + 254) / 255;
4110 : }
4111 : }
4112 : }
4113 :
4114 : void
4115 0 : nsCanvasRenderingContext2DAzure::FillRuleChanged()
4116 : {
4117 0 : if (mPath) {
4118 0 : mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
4119 0 : mPath = nsnull;
4120 : }
4121 0 : }
4122 :
4123 : // void putImageData (in ImageData d, in float x, in float y);
4124 : NS_IMETHODIMP
4125 0 : nsCanvasRenderingContext2DAzure::PutImageData()
4126 : {
4127 : /* Should never be called -- PutImageData_explicit is the QS entry point */
4128 0 : return NS_ERROR_NOT_IMPLEMENTED;
4129 : }
4130 :
4131 : NS_IMETHODIMP
4132 0 : nsCanvasRenderingContext2DAzure::PutImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 w, PRUint32 h,
4133 : unsigned char *aData, PRUint32 aDataLen,
4134 : bool hasDirtyRect, PRInt32 dirtyX, PRInt32 dirtyY,
4135 : PRInt32 dirtyWidth, PRInt32 dirtyHeight)
4136 : {
4137 0 : if (!mValid) {
4138 0 : return NS_ERROR_FAILURE;
4139 : }
4140 :
4141 0 : if (w == 0 || h == 0) {
4142 0 : return NS_ERROR_DOM_SYNTAX_ERR;
4143 : }
4144 :
4145 0 : IntRect dirtyRect;
4146 0 : IntRect imageDataRect(0, 0, w, h);
4147 :
4148 0 : if (hasDirtyRect) {
4149 : // fix up negative dimensions
4150 0 : if (dirtyWidth < 0) {
4151 0 : NS_ENSURE_TRUE(dirtyWidth != INT_MIN, NS_ERROR_DOM_INDEX_SIZE_ERR);
4152 :
4153 0 : CheckedInt32 checkedDirtyX = CheckedInt32(dirtyX) + dirtyWidth;
4154 :
4155 0 : if (!checkedDirtyX.valid())
4156 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
4157 :
4158 0 : dirtyX = checkedDirtyX.value();
4159 0 : dirtyWidth = -(int32)dirtyWidth;
4160 : }
4161 :
4162 0 : if (dirtyHeight < 0) {
4163 0 : NS_ENSURE_TRUE(dirtyHeight != INT_MIN, NS_ERROR_DOM_INDEX_SIZE_ERR);
4164 :
4165 0 : CheckedInt32 checkedDirtyY = CheckedInt32(dirtyY) + dirtyHeight;
4166 :
4167 0 : if (!checkedDirtyY.valid())
4168 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
4169 :
4170 0 : dirtyY = checkedDirtyY.value();
4171 0 : dirtyHeight = -(int32)dirtyHeight;
4172 : }
4173 :
4174 : // bound the dirty rect within the imageData rectangle
4175 0 : dirtyRect = imageDataRect.Intersect(IntRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight));
4176 :
4177 0 : if (dirtyRect.Width() <= 0 || dirtyRect.Height() <= 0)
4178 0 : return NS_OK;
4179 : } else {
4180 0 : dirtyRect = imageDataRect;
4181 : }
4182 :
4183 0 : dirtyRect.MoveBy(IntPoint(x, y));
4184 0 : dirtyRect = IntRect(0, 0, mWidth, mHeight).Intersect(dirtyRect);
4185 :
4186 0 : if (dirtyRect.Width() <= 0 || dirtyRect.Height() <= 0) {
4187 0 : return NS_OK;
4188 : }
4189 :
4190 0 : PRUint32 len = w * h * 4;
4191 0 : if (aDataLen != len) {
4192 0 : return NS_ERROR_DOM_SYNTAX_ERR;
4193 : }
4194 :
4195 : nsRefPtr<gfxImageSurface> imgsurf = new gfxImageSurface(gfxIntSize(w, h),
4196 0 : gfxASurface::ImageFormatARGB32);
4197 0 : if (!imgsurf || imgsurf->CairoStatus()) {
4198 0 : return NS_ERROR_FAILURE;
4199 : }
4200 :
4201 : // ensure premultiply table has been created
4202 0 : EnsurePremultiplyTable();
4203 :
4204 0 : PRUint8 *src = aData;
4205 0 : PRUint8 *dst = imgsurf->Data();
4206 :
4207 0 : for (PRUint32 j = 0; j < h; j++) {
4208 0 : for (PRUint32 i = 0; i < w; i++) {
4209 0 : PRUint8 r = *src++;
4210 0 : PRUint8 g = *src++;
4211 0 : PRUint8 b = *src++;
4212 0 : PRUint8 a = *src++;
4213 :
4214 : // Convert to premultiplied color (losslessly if the input came from getImageData)
4215 : #ifdef IS_LITTLE_ENDIAN
4216 0 : *dst++ = sPremultiplyTable[a][b];
4217 0 : *dst++ = sPremultiplyTable[a][g];
4218 0 : *dst++ = sPremultiplyTable[a][r];
4219 0 : *dst++ = a;
4220 : #else
4221 : *dst++ = a;
4222 : *dst++ = sPremultiplyTable[a][r];
4223 : *dst++ = sPremultiplyTable[a][g];
4224 : *dst++ = sPremultiplyTable[a][b];
4225 : #endif
4226 : }
4227 : }
4228 :
4229 : RefPtr<SourceSurface> sourceSurface =
4230 0 : mTarget->CreateSourceSurfaceFromData(imgsurf->Data(), IntSize(w, h), imgsurf->Stride(), FORMAT_B8G8R8A8);
4231 :
4232 :
4233 0 : mTarget->CopySurface(sourceSurface,
4234 : IntRect(dirtyRect.x - x, dirtyRect.y - y,
4235 : dirtyRect.width, dirtyRect.height),
4236 0 : IntPoint(dirtyRect.x, dirtyRect.y));
4237 :
4238 0 : Redraw(mgfx::Rect(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height));
4239 :
4240 0 : return NS_OK;
4241 : }
4242 :
4243 : NS_IMETHODIMP
4244 0 : nsCanvasRenderingContext2DAzure::GetThebesSurface(gfxASurface **surface)
4245 : {
4246 0 : if (!mTarget) {
4247 : nsRefPtr<gfxASurface> tmpSurf =
4248 0 : gfxPlatform::GetPlatform()->CreateOffscreenSurface(gfxIntSize(1, 1), gfxASurface::CONTENT_COLOR_ALPHA);
4249 0 : *surface = tmpSurf.forget().get();
4250 0 : return NS_OK;
4251 : }
4252 :
4253 0 : if (!mThebesSurface) {
4254 : mThebesSurface =
4255 0 : gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
4256 :
4257 0 : if (!mThebesSurface) {
4258 0 : return NS_ERROR_FAILURE;
4259 : }
4260 : } else {
4261 : // Normally GetThebesSurfaceForDrawTarget will handle the flush, when
4262 : // we're returning a cached ThebesSurface we need to flush here.
4263 0 : mTarget->Flush();
4264 : }
4265 :
4266 0 : *surface = mThebesSurface;
4267 0 : NS_ADDREF(*surface);
4268 :
4269 0 : return NS_OK;
4270 : }
4271 :
4272 : NS_IMETHODIMP
4273 0 : nsCanvasRenderingContext2DAzure::CreateImageData()
4274 : {
4275 : /* Should never be called; handled entirely in the quickstub */
4276 0 : return NS_ERROR_NOT_IMPLEMENTED;
4277 : }
4278 :
4279 : NS_IMETHODIMP
4280 0 : nsCanvasRenderingContext2DAzure::GetMozImageSmoothingEnabled(bool *retVal)
4281 : {
4282 0 : *retVal = CurrentState().imageSmoothingEnabled;
4283 0 : return NS_OK;
4284 : }
4285 :
4286 : NS_IMETHODIMP
4287 0 : nsCanvasRenderingContext2DAzure::SetMozImageSmoothingEnabled(bool val)
4288 : {
4289 0 : if (val != CurrentState().imageSmoothingEnabled) {
4290 0 : CurrentState().imageSmoothingEnabled = val;
4291 : }
4292 :
4293 0 : return NS_OK;
4294 : }
4295 :
4296 : static PRUint8 g2DContextLayerUserData;
4297 :
4298 0 : class CanvasRenderingContext2DUserData : public LayerUserData {
4299 : public:
4300 0 : CanvasRenderingContext2DUserData(nsHTMLCanvasElement *aContent)
4301 0 : : mContent(aContent) {}
4302 0 : static void DidTransactionCallback(void* aData)
4303 : {
4304 0 : static_cast<CanvasRenderingContext2DUserData*>(aData)->mContent->MarkContextClean();
4305 0 : }
4306 :
4307 : private:
4308 : nsRefPtr<nsHTMLCanvasElement> mContent;
4309 : };
4310 :
4311 : already_AddRefed<CanvasLayer>
4312 0 : nsCanvasRenderingContext2DAzure::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
4313 : CanvasLayer *aOldLayer,
4314 : LayerManager *aManager)
4315 : {
4316 0 : if (!mValid) {
4317 0 : return nsnull;
4318 : }
4319 :
4320 0 : if (mTarget) {
4321 0 : mTarget->Flush();
4322 : }
4323 :
4324 0 : if (!mResetLayer && aOldLayer &&
4325 0 : aOldLayer->HasUserData(&g2DContextLayerUserData)) {
4326 0 : NS_ADDREF(aOldLayer);
4327 0 : return aOldLayer;
4328 : }
4329 :
4330 0 : nsRefPtr<CanvasLayer> canvasLayer = aManager->CreateCanvasLayer();
4331 0 : if (!canvasLayer) {
4332 0 : NS_WARNING("CreateCanvasLayer returned null!");
4333 0 : return nsnull;
4334 : }
4335 0 : CanvasRenderingContext2DUserData *userData = nsnull;
4336 0 : if (aBuilder->IsPaintingToWindow()) {
4337 : // Make the layer tell us whenever a transaction finishes (including
4338 : // the current transaction), so we can clear our invalidation state and
4339 : // start invalidating again. We need to do this for the layer that is
4340 : // being painted to a window (there shouldn't be more than one at a time,
4341 : // and if there is, flushing the invalidation state more often than
4342 : // necessary is harmless).
4343 :
4344 : // The layer will be destroyed when we tear down the presentation
4345 : // (at the latest), at which time this userData will be destroyed,
4346 : // releasing the reference to the element.
4347 : // The userData will receive DidTransactionCallbacks, which flush the
4348 : // the invalidation state to indicate that the canvas is up to date.
4349 0 : userData = new CanvasRenderingContext2DUserData(HTMLCanvasElement());
4350 : canvasLayer->SetDidTransactionCallback(
4351 0 : CanvasRenderingContext2DUserData::DidTransactionCallback, userData);
4352 : }
4353 0 : canvasLayer->SetUserData(&g2DContextLayerUserData, userData);
4354 :
4355 0 : CanvasLayer::Data data;
4356 :
4357 0 : data.mDrawTarget = mTarget;
4358 0 : data.mSize = nsIntSize(mWidth, mHeight);
4359 :
4360 0 : canvasLayer->Initialize(data);
4361 0 : PRUint32 flags = mOpaque ? Layer::CONTENT_OPAQUE : 0;
4362 0 : canvasLayer->SetContentFlags(flags);
4363 0 : canvasLayer->Updated();
4364 :
4365 0 : mResetLayer = false;
4366 :
4367 0 : return canvasLayer.forget();
4368 : }
4369 :
4370 : void
4371 0 : nsCanvasRenderingContext2DAzure::MarkContextClean()
4372 : {
4373 0 : if (mInvalidateCount > 0) {
4374 0 : mPredictManyRedrawCalls = mInvalidateCount > kCanvasMaxInvalidateCount;
4375 : }
4376 0 : mIsEntireFrameInvalid = false;
4377 0 : mInvalidateCount = 0;
4378 4188 : }
4379 :
|