Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / layout / svg / base / src / nsSVGPathGeometryFrame.cpp
blob9c9c0225ecb09ee00badf33d9667214d69a69751
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
15 * The Original Code is the Mozilla SVG project.
17 * The Initial Developer of the Original Code is
18 * Crocodile Clips Ltd..
19 * Portions created by the Initial Developer are Copyright (C) 2002
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsSVGPathGeometryFrame.h"
40 #include "nsGkAtoms.h"
41 #include "nsSVGMarkerFrame.h"
42 #include "nsSVGMatrix.h"
43 #include "nsSVGUtils.h"
44 #include "nsSVGEffects.h"
45 #include "nsSVGGraphicElement.h"
46 #include "nsSVGOuterSVGFrame.h"
47 #include "nsSVGRect.h"
48 #include "nsSVGPathGeometryElement.h"
49 #include "gfxContext.h"
51 //----------------------------------------------------------------------
52 // Implementation
54 nsIFrame*
55 NS_NewSVGPathGeometryFrame(nsIPresShell* aPresShell,
56 nsIContent* aContent,
57 nsStyleContext* aContext)
59 return new (aPresShell) nsSVGPathGeometryFrame(aContext);
62 //----------------------------------------------------------------------
63 // nsISupports methods
65 NS_INTERFACE_MAP_BEGIN(nsSVGPathGeometryFrame)
66 NS_INTERFACE_MAP_ENTRY(nsISVGChildFrame)
67 NS_INTERFACE_MAP_END_INHERITING(nsSVGPathGeometryFrameBase)
69 //----------------------------------------------------------------------
70 // nsIFrame methods
72 NS_IMETHODIMP
73 nsSVGPathGeometryFrame::AttributeChanged(PRInt32 aNameSpaceID,
74 nsIAtom* aAttribute,
75 PRInt32 aModType)
77 if (aNameSpaceID == kNameSpaceID_None &&
78 (static_cast<nsSVGPathGeometryElement*>
79 (mContent)->IsDependentAttribute(aAttribute) ||
80 aAttribute == nsGkAtoms::transform))
81 nsSVGUtils::UpdateGraphic(this);
83 return NS_OK;
86 /* virtual */ void
87 nsSVGPathGeometryFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
89 nsSVGPathGeometryFrameBase::DidSetStyleContext(aOldStyleContext);
91 nsSVGUtils::InvalidateCoveredRegion(this);
93 // XXX: we'd like to use the style_hint mechanism and the
94 // ContentStateChanged/AttributeChanged functions for style changes
95 // to get slightly finer granularity, but unfortunately the
96 // style_hints don't map very well onto svg. Here seems to be the
97 // best place to deal with style changes:
99 nsSVGUtils::UpdateGraphic(this);
102 nsIAtom *
103 nsSVGPathGeometryFrame::GetType() const
105 return nsGkAtoms::svgPathGeometryFrame;
108 //----------------------------------------------------------------------
109 // nsISVGChildFrame methods
111 NS_IMETHODIMP
112 nsSVGPathGeometryFrame::PaintSVG(nsSVGRenderState *aContext,
113 const nsIntRect *aDirtyRect)
115 if (!GetStyleVisibility()->IsVisible())
116 return NS_OK;
118 /* render */
119 Render(aContext);
121 if (static_cast<nsSVGPathGeometryElement*>(mContent)->IsMarkable()) {
122 MarkerProperties properties = GetMarkerProperties(this);
124 if (properties.MarkersExist()) {
125 float strokeWidth = GetStrokeWidth();
127 nsTArray<nsSVGMark> marks;
128 static_cast<nsSVGPathGeometryElement*>
129 (mContent)->GetMarkPoints(&marks);
131 PRUint32 num = marks.Length();
133 if (num) {
134 nsSVGMarkerFrame *frame = properties.GetMarkerStartFrame();
135 if (frame)
136 frame->PaintMark(aContext, this, &marks[0], strokeWidth);
138 frame = properties.GetMarkerMidFrame();
139 if (frame) {
140 for (PRUint32 i = 1; i < num - 1; i++)
141 frame->PaintMark(aContext, this, &marks[i], strokeWidth);
144 frame = properties.GetMarkerEndFrame();
145 if (frame)
146 frame->PaintMark(aContext, this, &marks[num-1], strokeWidth);
151 return NS_OK;
154 NS_IMETHODIMP_(nsIFrame*)
155 nsSVGPathGeometryFrame::GetFrameForPoint(const nsPoint &aPoint)
157 PRUint16 fillRule, mask;
158 // check if we're a clipPath - cheaper than IsClipChild(), and we shouldn't
159 // get in here for other nondisplay children
160 if (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD) {
161 NS_ASSERTION(IsClipChild(), "should be in clipPath but we're not");
162 mask = HITTEST_MASK_FILL;
163 fillRule = GetClipRule();
164 } else {
165 mask = GetHittestMask();
166 if (!mask || (!(mask & HITTEST_MASK_FORCE_TEST) &&
167 !mRect.Contains(aPoint)))
168 return nsnull;
169 fillRule = GetStyleSVG()->mFillRule;
172 PRBool isHit = PR_FALSE;
174 gfxContext context(nsSVGUtils::GetThebesComputationalSurface());
176 GeneratePath(&context);
177 gfxPoint userSpacePoint =
178 context.DeviceToUser(gfxPoint(PresContext()->AppUnitsToGfxUnits(aPoint.x),
179 PresContext()->AppUnitsToGfxUnits(aPoint.y)));
181 if (fillRule == NS_STYLE_FILL_RULE_EVENODD)
182 context.SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
183 else
184 context.SetFillRule(gfxContext::FILL_RULE_WINDING);
186 if (mask & HITTEST_MASK_FILL)
187 isHit = context.PointInFill(userSpacePoint);
188 if (!isHit && (mask & HITTEST_MASK_STROKE) &&
189 SetupCairoStrokeHitGeometry(&context)) {
190 isHit = context.PointInStroke(userSpacePoint);
193 if (isHit && nsSVGUtils::HitTestClip(this, aPoint))
194 return this;
196 return nsnull;
199 NS_IMETHODIMP_(nsRect)
200 nsSVGPathGeometryFrame::GetCoveredRegion()
202 if (static_cast<nsSVGPathGeometryElement*>(mContent)->IsMarkable()) {
203 MarkerProperties properties = GetMarkerProperties(this);
205 if (!properties.MarkersExist())
206 return mRect;
208 nsRect rect(mRect);
210 float strokeWidth = GetStrokeWidth();
212 nsTArray<nsSVGMark> marks;
213 static_cast<nsSVGPathGeometryElement*>(mContent)->GetMarkPoints(&marks);
215 PRUint32 num = marks.Length();
217 if (num) {
218 nsSVGMarkerFrame *frame = properties.GetMarkerStartFrame();
219 if (frame) {
220 nsRect mark = frame->RegionMark(this, &marks[0], strokeWidth);
221 rect.UnionRect(rect, mark);
224 frame = properties.GetMarkerMidFrame();
225 if (frame) {
226 for (PRUint32 i = 1; i < num - 1; i++) {
227 nsRect mark = frame->RegionMark(this, &marks[i], strokeWidth);
228 rect.UnionRect(rect, mark);
232 frame = properties.GetMarkerEndFrame();
233 if (frame) {
234 nsRect mark = frame->RegionMark(this, &marks[num-1], strokeWidth);
235 rect.UnionRect(rect, mark);
239 return rect;
242 return mRect;
245 NS_IMETHODIMP
246 nsSVGPathGeometryFrame::UpdateCoveredRegion()
248 mRect.Empty();
250 gfxContext context(nsSVGUtils::GetThebesComputationalSurface());
252 static_cast<nsSVGPathGeometryElement*>(mContent)->ConstructPath(&context);
254 gfxRect extent = gfxRect(0, 0, 0, 0);
256 if (SetupCairoStrokeGeometry(&context)) {
257 extent = context.GetUserStrokeExtent();
259 if (GetStyleSVG()->mFill.mType != eStyleSVGPaintType_None) {
260 extent = extent.Union(context.GetUserPathExtent());
263 if (!extent.IsEmpty()) {
264 nsCOMPtr<nsIDOMSVGMatrix> ctm;
265 GetCanvasTM(getter_AddRefs(ctm));
266 NS_ASSERTION(ctm, "graphic source didn't specify a ctm");
268 gfxMatrix matrix = nsSVGUtils::ConvertSVGMatrixToThebes(ctm);
270 extent = matrix.TransformBounds(extent);
271 mRect = nsSVGUtils::ToAppPixelRect(PresContext(), extent);
274 // Add in markers
275 mRect = GetCoveredRegion();
277 return NS_OK;
280 NS_IMETHODIMP
281 nsSVGPathGeometryFrame::InitialUpdate()
283 NS_ASSERTION(GetStateBits() & NS_FRAME_FIRST_REFLOW,
284 "Yikes! We've been called already! Hopefully we weren't called "
285 "before our nsSVGOuterSVGFrame's initial Reflow()!!!");
287 nsSVGUtils::UpdateGraphic(this);
289 NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW),
290 "We don't actually participate in reflow");
292 // Do unset the various reflow bits, though.
293 mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
294 NS_FRAME_HAS_DIRTY_CHILDREN);
295 return NS_OK;
298 void
299 nsSVGPathGeometryFrame::NotifySVGChanged(PRUint32 aFlags)
301 if (!(aFlags & SUPPRESS_INVALIDATION)) {
302 nsSVGUtils::UpdateGraphic(this);
306 NS_IMETHODIMP
307 nsSVGPathGeometryFrame::NotifyRedrawSuspended()
309 // XXX should we cache the fact that redraw is suspended?
310 return NS_OK;
313 NS_IMETHODIMP
314 nsSVGPathGeometryFrame::NotifyRedrawUnsuspended()
316 if (GetStateBits() & NS_STATE_SVG_DIRTY)
317 nsSVGUtils::UpdateGraphic(this);
319 return NS_OK;
322 NS_IMETHODIMP
323 nsSVGPathGeometryFrame::SetMatrixPropagation(PRBool aPropagate)
325 if (aPropagate) {
326 AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
327 } else {
328 RemoveStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
330 return NS_OK;
333 PRBool
334 nsSVGPathGeometryFrame::GetMatrixPropagation()
336 return (GetStateBits() & NS_STATE_SVG_PROPAGATE_TRANSFORM) != 0;
339 NS_IMETHODIMP
340 nsSVGPathGeometryFrame::GetBBox(nsIDOMSVGRect **_retval)
342 gfxContext context(nsSVGUtils::GetThebesComputationalSurface());
344 GeneratePath(&context);
345 context.IdentityMatrix();
347 return NS_NewSVGRect(_retval, context.GetUserPathExtent());
350 //----------------------------------------------------------------------
351 // nsSVGGeometryFrame methods:
353 /* readonly attribute nsIDOMSVGMatrix canvasTM; */
354 NS_IMETHODIMP
355 nsSVGPathGeometryFrame::GetCanvasTM(nsIDOMSVGMatrix * *aCTM)
357 *aCTM = nsnull;
359 if (!GetMatrixPropagation()) {
360 return NS_NewSVGMatrix(aCTM);
363 nsSVGContainerFrame *containerFrame = static_cast<nsSVGContainerFrame*>
364 (mParent);
365 nsCOMPtr<nsIDOMSVGMatrix> parentTM = containerFrame->GetCanvasTM();
366 NS_ASSERTION(parentTM, "null TM");
368 // append our local transformations if we have any:
369 nsSVGGraphicElement *element =
370 static_cast<nsSVGGraphicElement*>(mContent);
371 nsCOMPtr<nsIDOMSVGMatrix> localTM = element->GetLocalTransformMatrix();
373 if (localTM)
374 return parentTM->Multiply(localTM, aCTM);
376 *aCTM = parentTM;
377 NS_ADDREF(*aCTM);
378 return NS_OK;
381 //----------------------------------------------------------------------
382 // nsSVGPathGeometryFrame methods:
384 nsSVGPathGeometryFrame::MarkerProperties
385 nsSVGPathGeometryFrame::GetMarkerProperties(nsSVGPathGeometryFrame *aFrame)
387 NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation");
389 MarkerProperties result;
390 const nsStyleSVG *style = aFrame->GetStyleSVG();
391 result.mMarkerStart = nsSVGEffects::GetMarkerProperty(
392 style->mMarkerStart, aFrame, nsGkAtoms::marker_start);
393 result.mMarkerMid = nsSVGEffects::GetMarkerProperty(
394 style->mMarkerMid, aFrame, nsGkAtoms::marker_mid);
395 result.mMarkerEnd = nsSVGEffects::GetMarkerProperty(
396 style->mMarkerEnd, aFrame, nsGkAtoms::marker_end);
397 return result;
400 nsSVGMarkerFrame *
401 nsSVGPathGeometryFrame::MarkerProperties::GetMarkerStartFrame()
403 if (!mMarkerStart)
404 return nsnull;
405 return static_cast<nsSVGMarkerFrame *>
406 (mMarkerStart->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nsnull));
409 nsSVGMarkerFrame *
410 nsSVGPathGeometryFrame::MarkerProperties::GetMarkerMidFrame()
412 if (!mMarkerMid)
413 return nsnull;
414 return static_cast<nsSVGMarkerFrame *>
415 (mMarkerMid->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nsnull));
418 nsSVGMarkerFrame *
419 nsSVGPathGeometryFrame::MarkerProperties::GetMarkerEndFrame()
421 if (!mMarkerEnd)
422 return nsnull;
423 return static_cast<nsSVGMarkerFrame *>
424 (mMarkerEnd->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nsnull));
427 void
428 nsSVGPathGeometryFrame::Render(nsSVGRenderState *aContext)
430 gfxContext *gfx = aContext->GetGfxContext();
432 PRUint16 renderMode = aContext->GetRenderMode();
434 /* save/pop the state so we don't screw up the xform */
435 gfx->Save();
437 GeneratePath(gfx);
439 if (renderMode != nsSVGRenderState::NORMAL) {
440 gfx->Restore();
442 if (GetClipRule() == NS_STYLE_FILL_RULE_EVENODD)
443 gfx->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
444 else
445 gfx->SetFillRule(gfxContext::FILL_RULE_WINDING);
447 if (renderMode == nsSVGRenderState::CLIP_MASK) {
448 gfx->SetAntialiasMode(gfxContext::MODE_ALIASED);
449 gfx->SetColor(gfxRGBA(1.0f, 1.0f, 1.0f, 1.0f));
450 gfx->Fill();
451 gfx->NewPath();
454 return;
457 switch (GetStyleSVG()->mShapeRendering) {
458 case NS_STYLE_SHAPE_RENDERING_OPTIMIZESPEED:
459 case NS_STYLE_SHAPE_RENDERING_CRISPEDGES:
460 gfx->SetAntialiasMode(gfxContext::MODE_ALIASED);
461 break;
462 default:
463 gfx->SetAntialiasMode(gfxContext::MODE_COVERAGE);
464 break;
467 if (SetupCairoFill(gfx)) {
468 gfx->Fill();
471 if (SetupCairoStroke(gfx)) {
472 gfx->Stroke();
475 gfx->NewPath();
477 gfx->Restore();
480 void
481 nsSVGPathGeometryFrame::GeneratePath(gfxContext* aContext)
483 nsCOMPtr<nsIDOMSVGMatrix> ctm;
484 GetCanvasTM(getter_AddRefs(ctm));
485 NS_ASSERTION(ctm, "graphic source didn't specify a ctm");
487 gfxMatrix matrix = nsSVGUtils::ConvertSVGMatrixToThebes(ctm);
489 if (matrix.IsSingular()) {
490 aContext->IdentityMatrix();
491 aContext->NewPath();
492 return;
495 aContext->Multiply(matrix);
497 aContext->NewPath();
498 static_cast<nsSVGPathGeometryElement*>(mContent)->ConstructPath(aContext);
501 PRUint16
502 nsSVGPathGeometryFrame::GetHittestMask()
504 PRUint16 mask = 0;
506 switch(GetStyleSVG()->mPointerEvents) {
507 case NS_STYLE_POINTER_EVENTS_NONE:
508 break;
509 case NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED:
510 if (GetStyleVisibility()->IsVisible()) {
511 if (GetStyleSVG()->mFill.mType != eStyleSVGPaintType_None)
512 mask |= HITTEST_MASK_FILL;
513 if (GetStyleSVG()->mStroke.mType != eStyleSVGPaintType_None)
514 mask |= HITTEST_MASK_STROKE;
516 break;
517 case NS_STYLE_POINTER_EVENTS_VISIBLEFILL:
518 if (GetStyleVisibility()->IsVisible()) {
519 mask |= HITTEST_MASK_FILL | HITTEST_MASK_FORCE_TEST;
521 break;
522 case NS_STYLE_POINTER_EVENTS_VISIBLESTROKE:
523 if (GetStyleVisibility()->IsVisible()) {
524 mask |= HITTEST_MASK_STROKE | HITTEST_MASK_FORCE_TEST;
526 break;
527 case NS_STYLE_POINTER_EVENTS_VISIBLE:
528 if (GetStyleVisibility()->IsVisible()) {
529 mask |=
530 HITTEST_MASK_FILL |
531 HITTEST_MASK_STROKE |
532 HITTEST_MASK_FORCE_TEST;
534 break;
535 case NS_STYLE_POINTER_EVENTS_PAINTED:
536 if (GetStyleSVG()->mFill.mType != eStyleSVGPaintType_None)
537 mask |= HITTEST_MASK_FILL;
538 if (GetStyleSVG()->mStroke.mType != eStyleSVGPaintType_None)
539 mask |= HITTEST_MASK_STROKE;
540 break;
541 case NS_STYLE_POINTER_EVENTS_FILL:
542 mask |= HITTEST_MASK_FILL | HITTEST_MASK_FORCE_TEST;
543 break;
544 case NS_STYLE_POINTER_EVENTS_STROKE:
545 mask |= HITTEST_MASK_STROKE | HITTEST_MASK_FORCE_TEST;
546 break;
547 case NS_STYLE_POINTER_EVENTS_ALL:
548 mask |=
549 HITTEST_MASK_FILL |
550 HITTEST_MASK_STROKE |
551 HITTEST_MASK_FORCE_TEST;
552 break;
553 default:
554 NS_ERROR("not reached");
555 break;
558 return mask;