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
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 the Mozilla SVG project.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Crocodile Clips Ltd..
19 : * Portions created by the Initial Developer are Copyright (C) 2001
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
24 : * Jonathan Watt <jonathan.watt@strath.ac.uk>
25 : * Chris Double <chris.double@double.co.nz>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "mozilla/Util.h"
42 :
43 : #include "nsGkAtoms.h"
44 : #include "DOMSVGNumber.h"
45 : #include "DOMSVGLength.h"
46 : #include "nsSVGAngle.h"
47 : #include "nsCOMPtr.h"
48 : #include "nsIPresShell.h"
49 : #include "nsContentUtils.h"
50 : #include "nsIDocument.h"
51 : #include "nsPresContext.h"
52 : #include "DOMSVGMatrix.h"
53 : #include "DOMSVGPoint.h"
54 : #include "nsIDOMEventTarget.h"
55 : #include "nsIFrame.h"
56 : #include "nsISVGSVGFrame.h" //XXX
57 : #include "nsSVGRect.h"
58 : #include "nsDOMError.h"
59 : #include "nsISVGChildFrame.h"
60 : #include "nsGUIEvent.h"
61 : #include "nsSVGUtils.h"
62 : #include "nsSVGSVGElement.h"
63 : #include "nsContentErrors.h" // For NS_PROPTABLE_PROP_OVERWRITTEN
64 : #include "nsContentUtils.h"
65 : #include "nsStyleUtil.h"
66 :
67 : #include "nsEventDispatcher.h"
68 : #include "nsSMILTimeContainer.h"
69 : #include "nsSMILAnimationController.h"
70 : #include "nsSMILTypes.h"
71 : #include "nsIContentIterator.h"
72 :
73 : nsresult NS_NewContentIterator(nsIContentIterator** aInstancePtrResult);
74 :
75 : using namespace mozilla;
76 : using namespace mozilla::dom;
77 :
78 1464 : NS_SVG_VAL_IMPL_CYCLE_COLLECTION(nsSVGTranslatePoint::DOMVal, mElement)
79 :
80 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGTranslatePoint::DOMVal)
81 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGTranslatePoint::DOMVal)
82 :
83 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGTranslatePoint::DOMVal)
84 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMSVGPoint)
85 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
86 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGPoint)
87 0 : NS_INTERFACE_MAP_END
88 :
89 : nsresult
90 0 : nsSVGTranslatePoint::ToDOMVal(nsSVGSVGElement *aElement,
91 : nsIDOMSVGPoint **aResult)
92 : {
93 0 : *aResult = new DOMVal(this, aElement);
94 0 : if (!*aResult)
95 0 : return NS_ERROR_OUT_OF_MEMORY;
96 :
97 0 : NS_ADDREF(*aResult);
98 0 : return NS_OK;
99 : }
100 :
101 : NS_IMETHODIMP
102 0 : nsSVGTranslatePoint::DOMVal::SetX(float aValue)
103 : {
104 0 : NS_ENSURE_FINITE(aValue, NS_ERROR_ILLEGAL_VALUE);
105 0 : return mElement->SetCurrentTranslate(aValue, mVal->GetY());
106 : }
107 :
108 : NS_IMETHODIMP
109 0 : nsSVGTranslatePoint::DOMVal::SetY(float aValue)
110 : {
111 0 : NS_ENSURE_FINITE(aValue, NS_ERROR_ILLEGAL_VALUE);
112 0 : return mElement->SetCurrentTranslate(mVal->GetX(), aValue);
113 : }
114 :
115 : /* nsIDOMSVGPoint matrixTransform (in nsIDOMSVGMatrix matrix); */
116 : NS_IMETHODIMP
117 0 : nsSVGTranslatePoint::DOMVal::MatrixTransform(nsIDOMSVGMatrix *matrix,
118 : nsIDOMSVGPoint **_retval)
119 : {
120 0 : if (!matrix)
121 0 : return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
122 :
123 : float a, b, c, d, e, f;
124 0 : matrix->GetA(&a);
125 0 : matrix->GetB(&b);
126 0 : matrix->GetC(&c);
127 0 : matrix->GetD(&d);
128 0 : matrix->GetE(&e);
129 0 : matrix->GetF(&f);
130 :
131 0 : float x = mVal->GetX();
132 0 : float y = mVal->GetY();
133 :
134 0 : NS_ADDREF(*_retval = new DOMSVGPoint(a*x + c*y + e, b*x + d*y + f));
135 0 : return NS_OK;
136 : }
137 :
138 : nsSVGElement::LengthInfo nsSVGSVGElement::sLengthInfo[4] =
139 : {
140 : { &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, nsSVGUtils::X },
141 : { &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, nsSVGUtils::Y },
142 : { &nsGkAtoms::width, 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, nsSVGUtils::X },
143 : { &nsGkAtoms::height, 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, nsSVGUtils::Y },
144 : };
145 :
146 : nsSVGEnumMapping nsSVGSVGElement::sZoomAndPanMap[] = {
147 : {&nsGkAtoms::disable, nsIDOMSVGZoomAndPan::SVG_ZOOMANDPAN_DISABLE},
148 : {&nsGkAtoms::magnify, nsIDOMSVGZoomAndPan::SVG_ZOOMANDPAN_MAGNIFY},
149 : {nsnull, 0}
150 : };
151 :
152 : nsSVGElement::EnumInfo nsSVGSVGElement::sEnumInfo[1] =
153 : {
154 : { &nsGkAtoms::zoomAndPan,
155 : sZoomAndPanMap,
156 : nsIDOMSVGZoomAndPan::SVG_ZOOMANDPAN_MAGNIFY
157 : }
158 : };
159 :
160 0 : NS_IMPL_NS_NEW_SVG_ELEMENT_CHECK_PARSER(SVG)
161 :
162 : //----------------------------------------------------------------------
163 : // nsISupports methods
164 :
165 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsSVGSVGElement)
166 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsSVGSVGElement,
167 : nsSVGSVGElementBase)
168 0 : if (tmp->mTimedDocumentRoot) {
169 0 : tmp->mTimedDocumentRoot->Unlink();
170 : }
171 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
172 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsSVGSVGElement,
173 : nsSVGSVGElementBase)
174 0 : if (tmp->mTimedDocumentRoot) {
175 0 : tmp->mTimedDocumentRoot->Traverse(&cb);
176 : }
177 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
178 :
179 0 : NS_IMPL_ADDREF_INHERITED(nsSVGSVGElement,nsSVGSVGElementBase)
180 0 : NS_IMPL_RELEASE_INHERITED(nsSVGSVGElement,nsSVGSVGElementBase)
181 :
182 0 : DOMCI_NODE_DATA(SVGSVGElement, nsSVGSVGElement)
183 :
184 0 : NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsSVGSVGElement)
185 0 : NS_NODE_INTERFACE_TABLE8(nsSVGSVGElement, nsIDOMNode, nsIDOMElement,
186 : nsIDOMSVGElement, nsIDOMSVGTests,
187 : nsIDOMSVGSVGElement,
188 : nsIDOMSVGFitToViewBox, nsIDOMSVGLocatable,
189 : nsIDOMSVGZoomAndPan)
190 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGSVGElement)
191 0 : NS_INTERFACE_MAP_END_INHERITING(nsSVGSVGElementBase)
192 :
193 : //----------------------------------------------------------------------
194 : // Implementation
195 :
196 0 : nsSVGSVGElement::nsSVGSVGElement(already_AddRefed<nsINodeInfo> aNodeInfo,
197 : FromParser aFromParser)
198 : : nsSVGSVGElementBase(aNodeInfo),
199 : mCoordCtx(nsnull),
200 : mViewportWidth(0),
201 : mViewportHeight(0),
202 : mCurrentTranslate(0.0f, 0.0f),
203 : mCurrentScale(1.0f),
204 : mPreviousTranslate(0.0f, 0.0f),
205 : mPreviousScale(1.0f),
206 : mRedrawSuspendCount(0),
207 0 : mStartAnimationOnBindToTree(!aFromParser),
208 : mImageNeedsTransformInvalidation(false),
209 0 : mIsPaintingSVGImageElement(false)
210 : {
211 0 : }
212 :
213 : //----------------------------------------------------------------------
214 : // nsIDOMNode methods
215 :
216 : // From NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGSVGElement)
217 : nsresult
218 0 : nsSVGSVGElement::Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const
219 : {
220 0 : *aResult = nsnull;
221 0 : nsCOMPtr<nsINodeInfo> ni = aNodeInfo;
222 0 : nsSVGSVGElement *it = new nsSVGSVGElement(ni.forget(), NOT_FROM_PARSER);
223 :
224 0 : nsCOMPtr<nsINode> kungFuDeathGrip = it;
225 0 : nsresult rv = it->Init();
226 0 : rv |= CopyInnerTo(it);
227 0 : if (NS_SUCCEEDED(rv)) {
228 0 : kungFuDeathGrip.swap(*aResult);
229 : }
230 :
231 0 : return rv;
232 : }
233 :
234 :
235 : //----------------------------------------------------------------------
236 : // nsIDOMSVGSVGElement methods:
237 :
238 : /* readonly attribute nsIDOMSVGAnimatedLength x; */
239 : NS_IMETHODIMP
240 0 : nsSVGSVGElement::GetX(nsIDOMSVGAnimatedLength * *aX)
241 : {
242 0 : return mLengthAttributes[X].ToDOMAnimatedLength(aX, this);
243 : }
244 :
245 : /* readonly attribute nsIDOMSVGAnimatedLength y; */
246 : NS_IMETHODIMP
247 0 : nsSVGSVGElement::GetY(nsIDOMSVGAnimatedLength * *aY)
248 : {
249 0 : return mLengthAttributes[Y].ToDOMAnimatedLength(aY, this);
250 : }
251 :
252 : /* readonly attribute nsIDOMSVGAnimatedLength width; */
253 : NS_IMETHODIMP
254 0 : nsSVGSVGElement::GetWidth(nsIDOMSVGAnimatedLength * *aWidth)
255 : {
256 0 : return mLengthAttributes[WIDTH].ToDOMAnimatedLength(aWidth, this);
257 : }
258 :
259 : /* readonly attribute nsIDOMSVGAnimatedLength height; */
260 : NS_IMETHODIMP
261 0 : nsSVGSVGElement::GetHeight(nsIDOMSVGAnimatedLength * *aHeight)
262 : {
263 0 : return mLengthAttributes[HEIGHT].ToDOMAnimatedLength(aHeight, this);
264 : }
265 :
266 : /* attribute DOMString contentScriptType; */
267 : NS_IMETHODIMP
268 0 : nsSVGSVGElement::GetContentScriptType(nsAString & aContentScriptType)
269 : {
270 0 : NS_NOTYETIMPLEMENTED("nsSVGSVGElement::GetContentScriptType");
271 0 : return NS_ERROR_NOT_IMPLEMENTED;
272 : }
273 : NS_IMETHODIMP
274 0 : nsSVGSVGElement::SetContentScriptType(const nsAString & aContentScriptType)
275 : {
276 0 : NS_NOTYETIMPLEMENTED("nsSVGSVGElement::SetContentScriptType");
277 0 : return NS_ERROR_NOT_IMPLEMENTED;
278 : }
279 :
280 : /* attribute DOMString contentStyleType; */
281 : NS_IMETHODIMP
282 0 : nsSVGSVGElement::GetContentStyleType(nsAString & aContentStyleType)
283 : {
284 0 : NS_NOTYETIMPLEMENTED("nsSVGSVGElement::GetContentStyleType");
285 0 : return NS_ERROR_NOT_IMPLEMENTED;
286 : }
287 : NS_IMETHODIMP
288 0 : nsSVGSVGElement::SetContentStyleType(const nsAString & aContentStyleType)
289 : {
290 0 : NS_NOTYETIMPLEMENTED("nsSVGSVGElement::SetContentStyleType");
291 0 : return NS_ERROR_NOT_IMPLEMENTED;
292 : }
293 :
294 : /* readonly attribute nsIDOMSVGRect viewport; */
295 : NS_IMETHODIMP
296 0 : nsSVGSVGElement::GetViewport(nsIDOMSVGRect * *aViewport)
297 : {
298 : // XXX
299 0 : return NS_ERROR_NOT_IMPLEMENTED;
300 : }
301 :
302 : /* readonly attribute float pixelUnitToMillimeterX; */
303 : NS_IMETHODIMP
304 0 : nsSVGSVGElement::GetPixelUnitToMillimeterX(float *aPixelUnitToMillimeterX)
305 : {
306 0 : *aPixelUnitToMillimeterX = MM_PER_INCH_FLOAT / 96;
307 0 : return NS_OK;
308 : }
309 :
310 : /* readonly attribute float pixelUnitToMillimeterY; */
311 : NS_IMETHODIMP
312 0 : nsSVGSVGElement::GetPixelUnitToMillimeterY(float *aPixelUnitToMillimeterY)
313 : {
314 0 : return GetPixelUnitToMillimeterX(aPixelUnitToMillimeterY);
315 : }
316 :
317 : /* readonly attribute float screenPixelToMillimeterX; */
318 : NS_IMETHODIMP
319 0 : nsSVGSVGElement::GetScreenPixelToMillimeterX(float *aScreenPixelToMillimeterX)
320 : {
321 0 : *aScreenPixelToMillimeterX = MM_PER_INCH_FLOAT / 96;
322 0 : return NS_OK;
323 : }
324 :
325 : /* readonly attribute float screenPixelToMillimeterY; */
326 : NS_IMETHODIMP
327 0 : nsSVGSVGElement::GetScreenPixelToMillimeterY(float *aScreenPixelToMillimeterY)
328 : {
329 0 : return GetScreenPixelToMillimeterX(aScreenPixelToMillimeterY);
330 : }
331 :
332 : /* attribute boolean useCurrentView; */
333 : NS_IMETHODIMP
334 0 : nsSVGSVGElement::GetUseCurrentView(bool *aUseCurrentView)
335 : {
336 0 : NS_NOTYETIMPLEMENTED("nsSVGSVGElement::GetUseCurrentView");
337 0 : return NS_ERROR_NOT_IMPLEMENTED;
338 : }
339 : NS_IMETHODIMP
340 0 : nsSVGSVGElement::SetUseCurrentView(bool aUseCurrentView)
341 : {
342 0 : NS_NOTYETIMPLEMENTED("nsSVGSVGElement::SetUseCurrentView");
343 0 : return NS_ERROR_NOT_IMPLEMENTED;
344 : }
345 :
346 : /* readonly attribute nsIDOMSVGViewSpec currentView; */
347 : NS_IMETHODIMP
348 0 : nsSVGSVGElement::GetCurrentView(nsIDOMSVGViewSpec * *aCurrentView)
349 : {
350 0 : NS_NOTYETIMPLEMENTED("nsSVGSVGElement::GetCurrentView");
351 0 : return NS_ERROR_NOT_IMPLEMENTED;
352 : }
353 :
354 : /* attribute float currentScale; */
355 : NS_IMETHODIMP
356 0 : nsSVGSVGElement::GetCurrentScale(float *aCurrentScale)
357 : {
358 0 : *aCurrentScale = mCurrentScale;
359 0 : return NS_OK;
360 : }
361 :
362 : #define CURRENT_SCALE_MAX 16.0f
363 : #define CURRENT_SCALE_MIN 0.0625f
364 :
365 : NS_IMETHODIMP
366 0 : nsSVGSVGElement::SetCurrentScale(float aCurrentScale)
367 : {
368 : return SetCurrentScaleTranslate(aCurrentScale,
369 0 : mCurrentTranslate.GetX(), mCurrentTranslate.GetY());
370 : }
371 :
372 : /* readonly attribute nsIDOMSVGPoint currentTranslate; */
373 : NS_IMETHODIMP
374 0 : nsSVGSVGElement::GetCurrentTranslate(nsIDOMSVGPoint * *aCurrentTranslate)
375 : {
376 0 : return mCurrentTranslate.ToDOMVal(this, aCurrentTranslate);
377 : }
378 :
379 : /* unsigned long suspendRedraw (in unsigned long max_wait_milliseconds); */
380 : NS_IMETHODIMP
381 0 : nsSVGSVGElement::SuspendRedraw(PRUint32 max_wait_milliseconds, PRUint32 *_retval)
382 : {
383 0 : *_retval = 1;
384 :
385 0 : if (++mRedrawSuspendCount > 1)
386 0 : return NS_OK;
387 :
388 0 : nsIFrame* frame = GetPrimaryFrame();
389 0 : if (frame) {
390 0 : nsISVGSVGFrame* svgframe = do_QueryFrame(frame);
391 : // might fail this check if we've failed conditional processing
392 0 : if (svgframe) {
393 0 : svgframe->SuspendRedraw();
394 : }
395 : }
396 :
397 0 : return NS_OK;
398 : }
399 :
400 : /* void unsuspendRedraw (in unsigned long suspend_handle_id); */
401 : NS_IMETHODIMP
402 0 : nsSVGSVGElement::UnsuspendRedraw(PRUint32 suspend_handle_id)
403 : {
404 0 : if (mRedrawSuspendCount == 0) {
405 0 : return NS_ERROR_FAILURE;
406 : }
407 :
408 0 : if (mRedrawSuspendCount > 1) {
409 0 : --mRedrawSuspendCount;
410 0 : return NS_OK;
411 : }
412 :
413 0 : return UnsuspendRedrawAll();
414 : }
415 :
416 : /* void unsuspendRedrawAll (); */
417 : NS_IMETHODIMP
418 0 : nsSVGSVGElement::UnsuspendRedrawAll()
419 : {
420 0 : mRedrawSuspendCount = 0;
421 :
422 0 : nsIFrame* frame = GetPrimaryFrame();
423 0 : if (frame) {
424 0 : nsISVGSVGFrame* svgframe = do_QueryFrame(frame);
425 : // might fail this check if we've failed conditional processing
426 0 : if (svgframe) {
427 0 : svgframe->UnsuspendRedraw();
428 : }
429 : }
430 0 : return NS_OK;
431 : }
432 :
433 : /* void forceRedraw (); */
434 : NS_IMETHODIMP
435 0 : nsSVGSVGElement::ForceRedraw()
436 : {
437 0 : nsIDocument* doc = GetCurrentDoc();
438 0 : if (!doc) return NS_ERROR_FAILURE;
439 :
440 0 : doc->FlushPendingNotifications(Flush_Display);
441 :
442 0 : return NS_OK;
443 : }
444 :
445 : /* void pauseAnimations (); */
446 : NS_IMETHODIMP
447 0 : nsSVGSVGElement::PauseAnimations()
448 : {
449 0 : if (NS_SMILEnabled()) {
450 0 : if (mTimedDocumentRoot) {
451 0 : mTimedDocumentRoot->Pause(nsSMILTimeContainer::PAUSE_SCRIPT);
452 : }
453 : // else we're not the outermost <svg> or not bound to a tree, so silently fail
454 0 : return NS_OK;
455 : }
456 0 : NS_NOTYETIMPLEMENTED("nsSVGSVGElement::PauseAnimations");
457 0 : return NS_ERROR_NOT_IMPLEMENTED;
458 : }
459 :
460 : /* void unpauseAnimations (); */
461 : NS_IMETHODIMP
462 0 : nsSVGSVGElement::UnpauseAnimations()
463 : {
464 0 : if (NS_SMILEnabled()) {
465 0 : if (mTimedDocumentRoot) {
466 0 : mTimedDocumentRoot->Resume(nsSMILTimeContainer::PAUSE_SCRIPT);
467 : }
468 : // else we're not the outermost <svg> or not bound to a tree, so silently fail
469 0 : return NS_OK;
470 : }
471 0 : NS_NOTYETIMPLEMENTED("nsSVGSVGElement::UnpauseAnimations");
472 0 : return NS_ERROR_NOT_IMPLEMENTED;
473 : }
474 :
475 : /* boolean animationsPaused (); */
476 : NS_IMETHODIMP
477 0 : nsSVGSVGElement::AnimationsPaused(bool *_retval)
478 : {
479 0 : if (NS_SMILEnabled()) {
480 0 : nsSMILTimeContainer* root = GetTimedDocumentRoot();
481 0 : *_retval = root && root->IsPausedByType(nsSMILTimeContainer::PAUSE_SCRIPT);
482 0 : return NS_OK;
483 : }
484 0 : NS_NOTYETIMPLEMENTED("nsSVGSVGElement::AnimationsPaused");
485 0 : return NS_ERROR_NOT_IMPLEMENTED;
486 : }
487 :
488 : /* float getCurrentTime (); */
489 : NS_IMETHODIMP
490 0 : nsSVGSVGElement::GetCurrentTime(float *_retval)
491 : {
492 0 : if (NS_SMILEnabled()) {
493 0 : nsSMILTimeContainer* root = GetTimedDocumentRoot();
494 0 : if (root) {
495 0 : double fCurrentTimeMs = double(root->GetCurrentTime());
496 0 : *_retval = (float)(fCurrentTimeMs / PR_MSEC_PER_SEC);
497 : } else {
498 0 : *_retval = 0.f;
499 : }
500 0 : return NS_OK;
501 : }
502 0 : NS_NOTYETIMPLEMENTED("nsSVGSVGElement::GetCurrentTime");
503 0 : return NS_ERROR_NOT_IMPLEMENTED;
504 : }
505 :
506 : /* void setCurrentTime (in float seconds); */
507 : NS_IMETHODIMP
508 0 : nsSVGSVGElement::SetCurrentTime(float seconds)
509 : {
510 0 : NS_ENSURE_FINITE(seconds, NS_ERROR_ILLEGAL_VALUE);
511 0 : if (NS_SMILEnabled()) {
512 0 : if (mTimedDocumentRoot) {
513 : // Make sure the timegraph is up-to-date
514 0 : FlushAnimations();
515 0 : double fMilliseconds = double(seconds) * PR_MSEC_PER_SEC;
516 : // Round to nearest whole number before converting, to avoid precision
517 : // errors
518 0 : nsSMILTime lMilliseconds = PRInt64(NS_round(fMilliseconds));
519 0 : mTimedDocumentRoot->SetCurrentTime(lMilliseconds);
520 0 : AnimationNeedsResample();
521 : // Trigger synchronous sample now, to:
522 : // - Make sure we get an up-to-date paint after this method
523 : // - re-enable event firing (it got disabled during seeking, and it
524 : // doesn't get re-enabled until the first sample after the seek -- so
525 : // let's make that happen now.)
526 0 : FlushAnimations();
527 : } // else we're not the outermost <svg> or not bound to a tree, so silently
528 : // fail
529 0 : return NS_OK;
530 : }
531 0 : NS_NOTYETIMPLEMENTED("nsSVGSVGElement::SetCurrentTime");
532 0 : return NS_ERROR_NOT_IMPLEMENTED;
533 : }
534 :
535 : /* nsIDOMNodeList getIntersectionList (in nsIDOMSVGRect rect, in nsIDOMSVGElement referenceElement); */
536 : NS_IMETHODIMP
537 0 : nsSVGSVGElement::GetIntersectionList(nsIDOMSVGRect *rect,
538 : nsIDOMSVGElement *referenceElement,
539 : nsIDOMNodeList **_retval)
540 : {
541 : // null check when implementing - this method can be used by scripts!
542 : // if (!rect || !referenceElement)
543 : // return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
544 :
545 0 : NS_NOTYETIMPLEMENTED("nsSVGSVGElement::GetIntersectionList");
546 0 : return NS_ERROR_NOT_IMPLEMENTED;
547 : }
548 :
549 : /* nsIDOMNodeList getEnclosureList (in nsIDOMSVGRect rect, in nsIDOMSVGElement referenceElement); */
550 : NS_IMETHODIMP
551 0 : nsSVGSVGElement::GetEnclosureList(nsIDOMSVGRect *rect,
552 : nsIDOMSVGElement *referenceElement,
553 : nsIDOMNodeList **_retval)
554 : {
555 : // null check when implementing - this method can be used by scripts!
556 : // if (!rect || !referenceElement)
557 : // return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
558 :
559 0 : NS_NOTYETIMPLEMENTED("nsSVGSVGElement::GetEnclosureList");
560 0 : return NS_ERROR_NOT_IMPLEMENTED;
561 : }
562 :
563 : /* boolean checkIntersection (in nsIDOMSVGElement element, in nsIDOMSVGRect rect); */
564 : NS_IMETHODIMP
565 0 : nsSVGSVGElement::CheckIntersection(nsIDOMSVGElement *element,
566 : nsIDOMSVGRect *rect,
567 : bool *_retval)
568 : {
569 : // null check when implementing - this method can be used by scripts!
570 : // if (!element || !rect)
571 : // return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
572 :
573 0 : NS_NOTYETIMPLEMENTED("nsSVGSVGElement::CheckIntersection");
574 0 : return NS_ERROR_NOT_IMPLEMENTED;
575 : }
576 :
577 : /* boolean checkEnclosure (in nsIDOMSVGElement element, in nsIDOMSVGRect rect); */
578 : NS_IMETHODIMP
579 0 : nsSVGSVGElement::CheckEnclosure(nsIDOMSVGElement *element,
580 : nsIDOMSVGRect *rect,
581 : bool *_retval)
582 : {
583 : // null check when implementing - this method can be used by scripts!
584 : // if (!element || !rect)
585 : // return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
586 :
587 0 : NS_NOTYETIMPLEMENTED("nsSVGSVGElement::CheckEnclosure");
588 0 : return NS_ERROR_NOT_IMPLEMENTED;
589 : }
590 :
591 : /* void deSelectAll (); */
592 : NS_IMETHODIMP
593 0 : nsSVGSVGElement::DeSelectAll()
594 : {
595 0 : NS_NOTYETIMPLEMENTED("nsSVGSVGElement::DeSelectAll");
596 0 : return NS_ERROR_NOT_IMPLEMENTED;
597 : }
598 :
599 : /* nsIDOMSVGNumber createSVGNumber (); */
600 : NS_IMETHODIMP
601 0 : nsSVGSVGElement::CreateSVGNumber(nsIDOMSVGNumber **_retval)
602 : {
603 0 : NS_ADDREF(*_retval = new DOMSVGNumber());
604 0 : return NS_OK;
605 : }
606 :
607 : /* nsIDOMSVGLength createSVGLength (); */
608 : NS_IMETHODIMP
609 0 : nsSVGSVGElement::CreateSVGLength(nsIDOMSVGLength **_retval)
610 : {
611 0 : NS_ADDREF(*_retval = new DOMSVGLength());
612 0 : return NS_OK;
613 : }
614 :
615 : /* nsIDOMSVGAngle createSVGAngle (); */
616 : NS_IMETHODIMP
617 0 : nsSVGSVGElement::CreateSVGAngle(nsIDOMSVGAngle **_retval)
618 : {
619 0 : return NS_NewDOMSVGAngle(_retval);
620 : }
621 :
622 : /* nsIDOMSVGPoint createSVGPoint (); */
623 : NS_IMETHODIMP
624 0 : nsSVGSVGElement::CreateSVGPoint(nsIDOMSVGPoint **_retval)
625 : {
626 0 : NS_ADDREF(*_retval = new DOMSVGPoint(0, 0));
627 0 : return NS_OK;
628 : }
629 :
630 : /* nsIDOMSVGMatrix createSVGMatrix (); */
631 : NS_IMETHODIMP
632 0 : nsSVGSVGElement::CreateSVGMatrix(nsIDOMSVGMatrix **_retval)
633 : {
634 0 : NS_ADDREF(*_retval = new DOMSVGMatrix());
635 0 : return NS_OK;
636 : }
637 :
638 : /* nsIDOMSVGRect createSVGRect (); */
639 : NS_IMETHODIMP
640 0 : nsSVGSVGElement::CreateSVGRect(nsIDOMSVGRect **_retval)
641 : {
642 0 : return NS_NewSVGRect(_retval);
643 : }
644 :
645 : /* nsIDOMSVGTransform createSVGTransform (); */
646 : NS_IMETHODIMP
647 0 : nsSVGSVGElement::CreateSVGTransform(nsIDOMSVGTransform **_retval)
648 : {
649 0 : NS_ADDREF(*_retval = new DOMSVGTransform());
650 0 : return NS_OK;
651 : }
652 :
653 : /* nsIDOMSVGTransform createSVGTransformFromMatrix (in nsIDOMSVGMatrix matrix); */
654 : NS_IMETHODIMP
655 0 : nsSVGSVGElement::CreateSVGTransformFromMatrix(nsIDOMSVGMatrix *matrix,
656 : nsIDOMSVGTransform **_retval)
657 : {
658 0 : nsCOMPtr<DOMSVGMatrix> domItem = do_QueryInterface(matrix);
659 0 : if (!domItem) {
660 0 : return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
661 : }
662 :
663 0 : NS_ADDREF(*_retval = new DOMSVGTransform(domItem->Matrix()));
664 0 : return NS_OK;
665 : }
666 :
667 : /* nsIDOMElement getElementById (in DOMString elementId); */
668 : NS_IMETHODIMP
669 0 : nsSVGSVGElement::GetElementById(const nsAString & elementId, nsIDOMElement **_retval)
670 : {
671 0 : NS_ENSURE_ARG_POINTER(_retval);
672 0 : *_retval = nsnull;
673 :
674 0 : nsresult rv = NS_OK;
675 0 : nsAutoString selector(NS_LITERAL_STRING("#"));
676 0 : nsStyleUtil::AppendEscapedCSSIdent(PromiseFlatString(elementId), selector);
677 0 : nsIContent* element = nsGenericElement::doQuerySelector(this, selector, &rv);
678 0 : if (NS_SUCCEEDED(rv) && element) {
679 0 : return CallQueryInterface(element, _retval);
680 : }
681 0 : return rv;
682 : }
683 :
684 : //----------------------------------------------------------------------
685 : // nsIDOMSVGFitToViewBox methods
686 :
687 : /* readonly attribute nsIDOMSVGAnimatedRect viewBox; */
688 : NS_IMETHODIMP
689 0 : nsSVGSVGElement::GetViewBox(nsIDOMSVGAnimatedRect * *aViewBox)
690 : {
691 0 : return mViewBox.ToDOMAnimatedRect(aViewBox, this);
692 : }
693 :
694 : /* readonly attribute nsIDOMSVGAnimatedPreserveAspectRatio preserveAspectRatio; */
695 : NS_IMETHODIMP
696 0 : nsSVGSVGElement::GetPreserveAspectRatio(nsIDOMSVGAnimatedPreserveAspectRatio
697 : **aPreserveAspectRatio)
698 : {
699 0 : return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(aPreserveAspectRatio, this);
700 : }
701 :
702 : //----------------------------------------------------------------------
703 : // nsIDOMSVGLocatable methods
704 :
705 : /* readonly attribute nsIDOMSVGElement nearestViewportElement; */
706 : NS_IMETHODIMP
707 0 : nsSVGSVGElement::GetNearestViewportElement(nsIDOMSVGElement * *aNearestViewportElement)
708 : {
709 0 : *aNearestViewportElement = nsSVGUtils::GetNearestViewportElement(this).get();
710 0 : return NS_OK;
711 : }
712 :
713 : /* readonly attribute nsIDOMSVGElement farthestViewportElement; */
714 : NS_IMETHODIMP
715 0 : nsSVGSVGElement::GetFarthestViewportElement(nsIDOMSVGElement * *aFarthestViewportElement)
716 : {
717 0 : NS_IF_ADDREF(*aFarthestViewportElement = nsSVGUtils::GetOuterSVGElement(this));
718 0 : return NS_OK;
719 : }
720 :
721 : /* nsIDOMSVGRect getBBox (); */
722 : NS_IMETHODIMP
723 0 : nsSVGSVGElement::GetBBox(nsIDOMSVGRect **_retval)
724 : {
725 0 : *_retval = nsnull;
726 :
727 0 : nsIFrame* frame = GetPrimaryFrame(Flush_Layout);
728 :
729 0 : if (!frame || (frame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD))
730 0 : return NS_ERROR_FAILURE;
731 :
732 0 : nsISVGChildFrame* svgframe = do_QueryFrame(frame);
733 0 : if (svgframe) {
734 0 : return NS_NewSVGRect(_retval, nsSVGUtils::GetBBox(frame));
735 : }
736 0 : return NS_ERROR_NOT_IMPLEMENTED; // XXX: outer svg
737 : }
738 :
739 : /* nsIDOMSVGMatrix getCTM (); */
740 : NS_IMETHODIMP
741 0 : nsSVGSVGElement::GetCTM(nsIDOMSVGMatrix * *aCTM)
742 : {
743 0 : gfxMatrix m = nsSVGUtils::GetCTM(this, false);
744 0 : *aCTM = m.IsSingular() ? nsnull : new DOMSVGMatrix(m);
745 0 : NS_IF_ADDREF(*aCTM);
746 0 : return NS_OK;
747 : }
748 :
749 : /* nsIDOMSVGMatrix getScreenCTM (); */
750 : NS_IMETHODIMP
751 0 : nsSVGSVGElement::GetScreenCTM(nsIDOMSVGMatrix **aCTM)
752 : {
753 0 : gfxMatrix m;
754 0 : if (IsRoot()) {
755 : // Consistency with other elements would have us return only the
756 : // eFromUserSpace transforms, but this is what we've been doing for
757 : // a while, and it keeps us consistent with WebKit and Opera (if not
758 : // really with the ambiguous spec).
759 0 : m = PrependLocalTransformsTo(m);
760 : } else {
761 0 : m = nsSVGUtils::GetCTM(this, true);
762 : }
763 0 : *aCTM = m.IsSingular() ? nsnull : new DOMSVGMatrix(m);
764 0 : NS_IF_ADDREF(*aCTM);
765 0 : return NS_OK;
766 : }
767 :
768 : /* nsIDOMSVGMatrix getTransformToElement (in nsIDOMSVGElement element); */
769 : NS_IMETHODIMP
770 0 : nsSVGSVGElement::GetTransformToElement(nsIDOMSVGElement *element,
771 : nsIDOMSVGMatrix **_retval)
772 : {
773 0 : if (!element)
774 0 : return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
775 :
776 : nsresult rv;
777 0 : *_retval = nsnull;
778 0 : nsCOMPtr<nsIDOMSVGMatrix> ourScreenCTM;
779 0 : nsCOMPtr<nsIDOMSVGMatrix> targetScreenCTM;
780 0 : nsCOMPtr<nsIDOMSVGMatrix> tmp;
781 0 : nsCOMPtr<nsIDOMSVGLocatable> target = do_QueryInterface(element, &rv);
782 0 : if (NS_FAILED(rv)) return rv;
783 :
784 : // the easiest way to do this (if likely to increase rounding error):
785 0 : GetScreenCTM(getter_AddRefs(ourScreenCTM));
786 0 : if (!ourScreenCTM) return NS_ERROR_DOM_SVG_MATRIX_NOT_INVERTABLE;
787 0 : target->GetScreenCTM(getter_AddRefs(targetScreenCTM));
788 0 : if (!targetScreenCTM) return NS_ERROR_DOM_SVG_MATRIX_NOT_INVERTABLE;
789 0 : rv = targetScreenCTM->Inverse(getter_AddRefs(tmp));
790 0 : if (NS_FAILED(rv)) return rv;
791 0 : return tmp->Multiply(ourScreenCTM, _retval); // addrefs, so we don't
792 : }
793 :
794 : //----------------------------------------------------------------------
795 : // nsIDOMSVGZoomAndPan methods
796 :
797 : /* attribute unsigned short zoomAndPan; */
798 : NS_IMETHODIMP
799 0 : nsSVGSVGElement::GetZoomAndPan(PRUint16 *aZoomAndPan)
800 : {
801 0 : *aZoomAndPan = mEnumAttributes[ZOOMANDPAN].GetAnimValue();
802 0 : return NS_OK;
803 : }
804 :
805 : NS_IMETHODIMP
806 0 : nsSVGSVGElement::SetZoomAndPan(PRUint16 aZoomAndPan)
807 : {
808 0 : if (aZoomAndPan == nsIDOMSVGZoomAndPan::SVG_ZOOMANDPAN_DISABLE ||
809 : aZoomAndPan == nsIDOMSVGZoomAndPan::SVG_ZOOMANDPAN_MAGNIFY) {
810 0 : mEnumAttributes[ZOOMANDPAN].SetBaseValue(aZoomAndPan, this);
811 0 : return NS_OK;
812 : }
813 :
814 0 : return NS_ERROR_DOM_SVG_INVALID_VALUE_ERR;
815 : }
816 :
817 : //----------------------------------------------------------------------
818 : // helper methods for implementing SVGZoomEvent:
819 :
820 : NS_IMETHODIMP
821 0 : nsSVGSVGElement::SetCurrentScaleTranslate(float s, float x, float y)
822 : {
823 0 : NS_ENSURE_FINITE3(s, x, y, NS_ERROR_ILLEGAL_VALUE);
824 :
825 0 : if (s == mCurrentScale &&
826 0 : x == mCurrentTranslate.GetX() && y == mCurrentTranslate.GetY()) {
827 0 : return NS_OK;
828 : }
829 :
830 : // Prevent bizarre behaviour and maxing out of CPU and memory by clamping
831 0 : if (s < CURRENT_SCALE_MIN)
832 0 : s = CURRENT_SCALE_MIN;
833 0 : else if (s > CURRENT_SCALE_MAX)
834 0 : s = CURRENT_SCALE_MAX;
835 :
836 : // IMPORTANT: If either mCurrentTranslate *or* mCurrentScale is changed then
837 : // mPreviousTranslate_x, mPreviousTranslate_y *and* mPreviousScale must all
838 : // be updated otherwise SVGZoomEvents will end up with invalid data. I.e. an
839 : // SVGZoomEvent's properties previousScale and previousTranslate must contain
840 : // the state of currentScale and currentTranslate immediately before the
841 : // change that caused the event's dispatch, which is *not* necessarily the
842 : // same thing as the values of currentScale and currentTranslate prior to
843 : // their own last change.
844 0 : mPreviousScale = mCurrentScale;
845 0 : mPreviousTranslate = mCurrentTranslate;
846 :
847 0 : mCurrentScale = s;
848 0 : mCurrentTranslate = nsSVGTranslatePoint(x, y);
849 :
850 : // now dispatch the appropriate event if we are the root element
851 0 : nsIDocument* doc = GetCurrentDoc();
852 0 : if (doc) {
853 0 : nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
854 0 : if (presShell && IsRoot()) {
855 0 : bool scaling = (mPreviousScale != mCurrentScale);
856 0 : nsEventStatus status = nsEventStatus_eIgnore;
857 0 : nsGUIEvent event(true, scaling ? NS_SVG_ZOOM : NS_SVG_SCROLL, 0);
858 0 : event.eventStructType = scaling ? NS_SVGZOOM_EVENT : NS_SVG_EVENT;
859 0 : presShell->HandleDOMEventWithTarget(this, &event, &status);
860 0 : InvalidateTransformNotifyFrame();
861 : }
862 : }
863 0 : return NS_OK;
864 : }
865 :
866 : NS_IMETHODIMP
867 0 : nsSVGSVGElement::SetCurrentTranslate(float x, float y)
868 : {
869 0 : return SetCurrentScaleTranslate(mCurrentScale, x, y);
870 : }
871 :
872 : nsSMILTimeContainer*
873 0 : nsSVGSVGElement::GetTimedDocumentRoot()
874 : {
875 0 : if (mTimedDocumentRoot) {
876 0 : return mTimedDocumentRoot;
877 : }
878 :
879 : // We must not be the outermost <svg> element, try to find it
880 : nsSVGSVGElement *outerSVGElement =
881 0 : nsSVGUtils::GetOuterSVGElement(this);
882 :
883 0 : if (outerSVGElement) {
884 0 : return outerSVGElement->GetTimedDocumentRoot();
885 : }
886 : // invalid structure
887 0 : return nsnull;
888 : }
889 :
890 : //----------------------------------------------------------------------
891 : // nsIContent methods
892 :
893 : NS_IMETHODIMP_(bool)
894 0 : nsSVGSVGElement::IsAttributeMapped(const nsIAtom* name) const
895 : {
896 : // We want to map the 'width' and 'height' attributes into style for
897 : // outer-<svg>, except when the attributes aren't set (since their default
898 : // values of '100%' can cause unexpected and undesirable behaviour for SVG
899 : // inline in HTML). We rely on nsSVGElement::UpdateContentStyleRule() to
900 : // prevent mapping of the default values into style (it only maps attributes
901 : // that are set). We also rely on a check in nsSVGElement::
902 : // UpdateContentStyleRule() to prevent us mapping the attributes when they're
903 : // given a <length> value that is not currently recognized by the SVG
904 : // specification.
905 :
906 0 : if (!IsInner() && (name == nsGkAtoms::width || name == nsGkAtoms::height)) {
907 0 : return true;
908 : }
909 :
910 : static const MappedAttributeEntry* const map[] = {
911 : sColorMap,
912 : sFEFloodMap,
913 : sFillStrokeMap,
914 : sFiltersMap,
915 : sFontSpecificationMap,
916 : sGradientStopMap,
917 : sGraphicsMap,
918 : sLightingEffectsMap,
919 : sMarkersMap,
920 : sTextContentElementsMap,
921 : sViewportsMap
922 : };
923 :
924 0 : return FindAttributeDependence(name, map) ||
925 0 : nsSVGSVGElementBase::IsAttributeMapped(name);
926 : }
927 :
928 : //----------------------------------------------------------------------
929 : // nsIContent methods:
930 :
931 : nsresult
932 0 : nsSVGSVGElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
933 : {
934 0 : if (aVisitor.mEvent->message == NS_SVG_LOAD) {
935 0 : if (mTimedDocumentRoot) {
936 0 : mTimedDocumentRoot->Begin();
937 : // Set 'resample needed' flag, so that if any script calls a DOM method
938 : // that requires up-to-date animations before our first sample callback,
939 : // we'll force a synchronous sample.
940 0 : AnimationNeedsResample();
941 : }
942 : }
943 0 : return nsSVGSVGElementBase::PreHandleEvent(aVisitor);
944 : }
945 :
946 : //----------------------------------------------------------------------
947 : // nsSVGElement overrides
948 :
949 : bool
950 0 : nsSVGSVGElement::IsEventName(nsIAtom* aName)
951 : {
952 : /* The events in EventNameType_SVGSVG are for events that are only
953 : applicable to outermost 'svg' elements. We don't check if we're an outer
954 : 'svg' element in case we're not inserted into the document yet, but since
955 : the target of the events in question will always be the outermost 'svg'
956 : element, this shouldn't cause any real problems.
957 : */
958 : return nsContentUtils::IsEventAttributeName(aName,
959 0 : (EventNameType_SVGGraphic | EventNameType_SVGSVG));
960 : }
961 :
962 : // Helper for GetViewBoxTransform on root <svg> node
963 : // * aLength: internal value for our <svg> width or height attribute.
964 : // * aViewportLength: length of the corresponding dimension of the viewport.
965 : // * aSelf: the outermost <svg> node itself.
966 : // NOTE: aSelf is not an ancestor viewport element, so it can't be used to
967 : // resolve percentage lengths. (It can only be used to resolve
968 : // 'em'/'ex'-valued units).
969 : inline float
970 0 : ComputeSynthesizedViewBoxDimension(const nsSVGLength2& aLength,
971 : float aViewportLength,
972 : const nsSVGSVGElement* aSelf)
973 : {
974 0 : if (aLength.IsPercentage()) {
975 0 : return aViewportLength * aLength.GetAnimValInSpecifiedUnits() / 100.0f;
976 : }
977 :
978 0 : return aLength.GetAnimValue(const_cast<nsSVGSVGElement*>(aSelf));
979 : }
980 :
981 : //----------------------------------------------------------------------
982 : // public helpers:
983 :
984 : gfxMatrix
985 0 : nsSVGSVGElement::GetViewBoxTransform() const
986 : {
987 : // Do we have an override preserveAspectRatio value?
988 : const SVGPreserveAspectRatio* overridePARPtr =
989 0 : GetImageOverridePreserveAspectRatio();
990 :
991 : // May assign this to overridePARPtr if we have no viewBox but are faking one:
992 0 : SVGPreserveAspectRatio tmpPAR;
993 :
994 : float viewportWidth, viewportHeight;
995 0 : if (IsInner()) {
996 0 : nsSVGSVGElement *ctx = GetCtx();
997 0 : viewportWidth = mLengthAttributes[WIDTH].GetAnimValue(ctx);
998 0 : viewportHeight = mLengthAttributes[HEIGHT].GetAnimValue(ctx);
999 : } else {
1000 0 : viewportWidth = mViewportWidth;
1001 0 : viewportHeight = mViewportHeight;
1002 : }
1003 :
1004 0 : if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) {
1005 0 : return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
1006 : }
1007 :
1008 0 : nsSVGViewBoxRect viewBox;
1009 0 : if (mViewBox.IsValid()) {
1010 0 : viewBox = mViewBox.GetAnimValue();
1011 : } else {
1012 0 : viewBox.x = viewBox.y = 0.0f;
1013 0 : if (ShouldSynthesizeViewBox()) {
1014 : // Special case -- fake a viewBox, using height & width attrs.
1015 : // (Use |this| as context, since if we get here, we're outermost <svg>.)
1016 : viewBox.width =
1017 : ComputeSynthesizedViewBoxDimension(mLengthAttributes[WIDTH],
1018 0 : mViewportWidth, this);
1019 : viewBox.height =
1020 : ComputeSynthesizedViewBoxDimension(mLengthAttributes[HEIGHT],
1021 0 : mViewportHeight, this);
1022 0 : NS_ABORT_IF_FALSE(!overridePARPtr,
1023 : "shouldn't have overridePAR if we're "
1024 : "synthesizing a viewBox");
1025 :
1026 : // If we're synthesizing a viewBox, use preserveAspectRatio="none";
1027 0 : tmpPAR.SetAlign(nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE);
1028 :
1029 : // (set the other pAR attributes too, just so they're initialized):
1030 0 : tmpPAR.SetDefer(false);
1031 0 : tmpPAR.SetMeetOrSlice(nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE);
1032 :
1033 0 : overridePARPtr = &tmpPAR;
1034 : } else {
1035 : // No viewBox attribute, so we shouldn't auto-scale. This is equivalent
1036 : // to having a viewBox that exactly matches our viewport size.
1037 0 : viewBox.width = viewportWidth;
1038 0 : viewBox.height = viewportHeight;
1039 : }
1040 : }
1041 :
1042 0 : if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) {
1043 0 : return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
1044 : }
1045 :
1046 : return nsSVGUtils::GetViewBoxTransform(this,
1047 : viewportWidth, viewportHeight,
1048 : viewBox.x, viewBox.y,
1049 : viewBox.width, viewBox.height,
1050 : overridePARPtr ? *overridePARPtr :
1051 0 : mPreserveAspectRatio.GetAnimValue());
1052 : }
1053 :
1054 : nsresult
1055 0 : nsSVGSVGElement::BindToTree(nsIDocument* aDocument,
1056 : nsIContent* aParent,
1057 : nsIContent* aBindingParent,
1058 : bool aCompileEventHandlers)
1059 : {
1060 0 : nsSMILAnimationController* smilController = nsnull;
1061 :
1062 0 : if (aDocument) {
1063 0 : smilController = aDocument->GetAnimationController();
1064 0 : if (smilController) {
1065 : // SMIL is enabled in this document
1066 0 : if (WillBeOutermostSVG(aParent, aBindingParent)) {
1067 : // We'll be the outermost <svg> element. We'll need a time container.
1068 0 : if (!mTimedDocumentRoot) {
1069 0 : mTimedDocumentRoot = new nsSMILTimeContainer();
1070 0 : NS_ENSURE_TRUE(mTimedDocumentRoot, NS_ERROR_OUT_OF_MEMORY);
1071 : }
1072 : } else {
1073 : // We're a child of some other <svg> element, so we don't need our own
1074 : // time container. However, we need to make sure that we'll get a
1075 : // kick-start if we get promoted to be outermost later on.
1076 0 : mTimedDocumentRoot = nsnull;
1077 0 : mStartAnimationOnBindToTree = true;
1078 : }
1079 : }
1080 : }
1081 :
1082 : nsresult rv = nsSVGSVGElementBase::BindToTree(aDocument, aParent,
1083 : aBindingParent,
1084 0 : aCompileEventHandlers);
1085 0 : NS_ENSURE_SUCCESS(rv,rv);
1086 :
1087 0 : if (mTimedDocumentRoot && smilController) {
1088 0 : rv = mTimedDocumentRoot->SetParent(smilController);
1089 0 : if (mStartAnimationOnBindToTree) {
1090 0 : mTimedDocumentRoot->Begin();
1091 0 : mStartAnimationOnBindToTree = false;
1092 : }
1093 : }
1094 :
1095 0 : return rv;
1096 : }
1097 :
1098 : void
1099 0 : nsSVGSVGElement::UnbindFromTree(bool aDeep, bool aNullParent)
1100 : {
1101 0 : if (mTimedDocumentRoot) {
1102 0 : mTimedDocumentRoot->SetParent(nsnull);
1103 : }
1104 :
1105 0 : nsSVGSVGElementBase::UnbindFromTree(aDeep, aNullParent);
1106 0 : }
1107 :
1108 : //----------------------------------------------------------------------
1109 : // implementation helpers
1110 :
1111 : bool
1112 0 : nsSVGSVGElement::WillBeOutermostSVG(nsIContent* aParent,
1113 : nsIContent* aBindingParent) const
1114 : {
1115 0 : nsIContent* parent = aBindingParent ? aBindingParent : aParent;
1116 :
1117 0 : while (parent && parent->IsSVG()) {
1118 0 : nsIAtom* tag = parent->Tag();
1119 0 : if (tag == nsGkAtoms::foreignObject) {
1120 : // SVG in a foreignObject must have its own <svg> (nsSVGOuterSVGFrame).
1121 0 : return false;
1122 : }
1123 0 : if (tag == nsGkAtoms::svg) {
1124 0 : return false;
1125 : }
1126 0 : parent = parent->GetParent();
1127 : }
1128 :
1129 0 : return true;
1130 : }
1131 :
1132 : void
1133 0 : nsSVGSVGElement::InvalidateTransformNotifyFrame()
1134 : {
1135 0 : nsIFrame* frame = GetPrimaryFrame();
1136 0 : if (frame) {
1137 0 : nsISVGSVGFrame* svgframe = do_QueryFrame(frame);
1138 : // might fail this check if we've failed conditional processing
1139 0 : if (svgframe) {
1140 0 : svgframe->NotifyViewportChange();
1141 : }
1142 : }
1143 0 : }
1144 :
1145 : bool
1146 0 : nsSVGSVGElement::HasPreserveAspectRatio()
1147 : {
1148 0 : return HasAttr(kNameSpaceID_None, nsGkAtoms::preserveAspectRatio) ||
1149 0 : mPreserveAspectRatio.IsAnimated();
1150 : }
1151 :
1152 : //----------------------------------------------------------------------
1153 : // nsSVGSVGElement
1154 :
1155 : float
1156 0 : nsSVGSVGElement::GetLength(PRUint8 aCtxType)
1157 : {
1158 : float h, w;
1159 :
1160 0 : if (mViewBox.IsValid()) {
1161 0 : const nsSVGViewBoxRect& viewbox = mViewBox.GetAnimValue();
1162 0 : w = viewbox.width;
1163 0 : h = viewbox.height;
1164 0 : } else if (IsInner()) {
1165 0 : nsSVGSVGElement *ctx = GetCtx();
1166 0 : w = mLengthAttributes[WIDTH].GetAnimValue(ctx);
1167 0 : h = mLengthAttributes[HEIGHT].GetAnimValue(ctx);
1168 0 : } else if (ShouldSynthesizeViewBox()) {
1169 : w = ComputeSynthesizedViewBoxDimension(mLengthAttributes[WIDTH],
1170 0 : mViewportWidth, this);
1171 : h = ComputeSynthesizedViewBoxDimension(mLengthAttributes[HEIGHT],
1172 0 : mViewportHeight, this);
1173 : } else {
1174 0 : w = mViewportWidth;
1175 0 : h = mViewportHeight;
1176 : }
1177 :
1178 0 : w = NS_MAX(w, 0.0f);
1179 0 : h = NS_MAX(h, 0.0f);
1180 :
1181 0 : switch (aCtxType) {
1182 : case nsSVGUtils::X:
1183 0 : return w;
1184 : case nsSVGUtils::Y:
1185 0 : return h;
1186 : case nsSVGUtils::XY:
1187 0 : return float(nsSVGUtils::ComputeNormalizedHypotenuse(w, h));
1188 : }
1189 0 : return 0;
1190 : }
1191 :
1192 : void
1193 0 : nsSVGSVGElement::SyncWidthOrHeight(nsIAtom* aName, nsSVGElement *aTarget) const
1194 : {
1195 0 : NS_ASSERTION(aName == nsGkAtoms::width || aName == nsGkAtoms::height,
1196 : "The clue is in the function name");
1197 :
1198 0 : PRUint32 index = *sLengthInfo[WIDTH].mName == aName ? WIDTH : HEIGHT;
1199 0 : aTarget->SetLength(aName, mLengthAttributes[index]);
1200 0 : }
1201 :
1202 : //----------------------------------------------------------------------
1203 : // nsSVGElement methods
1204 :
1205 : /* virtual */ gfxMatrix
1206 0 : nsSVGSVGElement::PrependLocalTransformsTo(const gfxMatrix &aMatrix,
1207 : TransformTypes aWhich) const
1208 : {
1209 0 : NS_ABORT_IF_FALSE(aWhich != eChildToUserSpace || aMatrix.IsIdentity(),
1210 : "Skipping eUserSpaceToParent transforms makes no sense");
1211 :
1212 0 : if (IsInner()) {
1213 : float x, y;
1214 0 : const_cast<nsSVGSVGElement*>(this)->GetAnimatedLengthValues(&x, &y, nsnull);
1215 0 : if (aWhich == eAllTransforms) {
1216 : // the common case
1217 0 : return GetViewBoxTransform() * gfxMatrix().Translate(gfxPoint(x, y)) * aMatrix;
1218 : }
1219 0 : if (aWhich == eUserSpaceToParent) {
1220 0 : return gfxMatrix().Translate(gfxPoint(x, y)) * aMatrix;
1221 : }
1222 0 : NS_ABORT_IF_FALSE(aWhich == eChildToUserSpace, "Unknown TransformTypes");
1223 0 : return GetViewBoxTransform(); // no need to multiply identity aMatrix
1224 : }
1225 :
1226 0 : if (aWhich == eUserSpaceToParent) {
1227 : // only inner-<svg> has eUserSpaceToParent transforms
1228 0 : return aMatrix;
1229 : }
1230 :
1231 0 : if (IsRoot()) {
1232 0 : gfxMatrix zoomPanTM;
1233 0 : zoomPanTM.Translate(gfxPoint(mCurrentTranslate.GetX(), mCurrentTranslate.GetY()));
1234 0 : zoomPanTM.Scale(mCurrentScale, mCurrentScale);
1235 0 : return GetViewBoxTransform() * zoomPanTM * aMatrix;
1236 : }
1237 :
1238 : // outer-<svg>, but inline in some other content:
1239 0 : return GetViewBoxTransform() * aMatrix;
1240 : }
1241 :
1242 : /* virtual */ bool
1243 0 : nsSVGSVGElement::HasValidDimensions() const
1244 : {
1245 0 : return !IsInner() ||
1246 0 : ((!mLengthAttributes[WIDTH].IsExplicitlySet() ||
1247 0 : mLengthAttributes[WIDTH].GetAnimValInSpecifiedUnits() > 0) &&
1248 0 : (!mLengthAttributes[HEIGHT].IsExplicitlySet() ||
1249 0 : mLengthAttributes[HEIGHT].GetAnimValInSpecifiedUnits() > 0));
1250 : }
1251 :
1252 : nsSVGElement::LengthAttributesInfo
1253 0 : nsSVGSVGElement::GetLengthInfo()
1254 : {
1255 : return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
1256 0 : ArrayLength(sLengthInfo));
1257 : }
1258 :
1259 : nsSVGElement::EnumAttributesInfo
1260 0 : nsSVGSVGElement::GetEnumInfo()
1261 : {
1262 : return EnumAttributesInfo(mEnumAttributes, sEnumInfo,
1263 0 : ArrayLength(sEnumInfo));
1264 : }
1265 :
1266 : nsSVGViewBox *
1267 0 : nsSVGSVGElement::GetViewBox()
1268 : {
1269 0 : return &mViewBox;
1270 : }
1271 :
1272 : SVGAnimatedPreserveAspectRatio *
1273 0 : nsSVGSVGElement::GetPreserveAspectRatio()
1274 : {
1275 0 : return &mPreserveAspectRatio;
1276 : }
1277 :
1278 : bool
1279 0 : nsSVGSVGElement::ShouldSynthesizeViewBox() const
1280 : {
1281 0 : NS_ABORT_IF_FALSE(!HasValidViewbox(),
1282 : "Should only be called if we lack a viewBox");
1283 :
1284 0 : nsIDocument* doc = GetCurrentDoc();
1285 : return doc &&
1286 0 : doc->IsBeingUsedAsImage() &&
1287 0 : !mIsPaintingSVGImageElement &&
1288 0 : !GetParent();
1289 : }
1290 :
1291 :
1292 : // Callback function, for freeing PRUint64 values stored in property table
1293 : static void
1294 0 : ReleasePreserveAspectRatioPropertyValue(void* aObject, /* unused */
1295 : nsIAtom* aPropertyName, /* unused */
1296 : void* aPropertyValue,
1297 : void* aData /* unused */)
1298 : {
1299 : SVGPreserveAspectRatio* valPtr =
1300 0 : static_cast<SVGPreserveAspectRatio*>(aPropertyValue);
1301 : delete valPtr;
1302 0 : }
1303 :
1304 : void
1305 0 : nsSVGSVGElement::
1306 : SetImageOverridePreserveAspectRatio(const SVGPreserveAspectRatio& aPAR)
1307 : {
1308 : #ifdef DEBUG
1309 0 : NS_ABORT_IF_FALSE(GetCurrentDoc()->IsBeingUsedAsImage(),
1310 : "should only override preserveAspectRatio in images");
1311 : #endif
1312 :
1313 0 : if (!HasValidViewbox() && ShouldSynthesizeViewBox()) {
1314 : // My non-<svg:image> clients will have been painting me with a synthesized
1315 : // viewBox, but my <svg:image> client that's about to paint me now does NOT
1316 : // want that. Need to tell ourselves to flush our transform.
1317 0 : mImageNeedsTransformInvalidation = true;
1318 : }
1319 0 : mIsPaintingSVGImageElement = true;
1320 :
1321 0 : if (!mViewBox.IsValid()) {
1322 0 : return; // preserveAspectRatio irrelevant (only matters if we have viewBox)
1323 : }
1324 :
1325 0 : if (aPAR.GetDefer() && HasPreserveAspectRatio()) {
1326 0 : return; // Referring element defers to my own preserveAspectRatio value.
1327 : }
1328 :
1329 0 : SVGPreserveAspectRatio* pAROverridePtr = new SVGPreserveAspectRatio(aPAR);
1330 : nsresult rv = SetProperty(nsGkAtoms::overridePreserveAspectRatio,
1331 : pAROverridePtr,
1332 0 : ReleasePreserveAspectRatioPropertyValue);
1333 0 : NS_ABORT_IF_FALSE(rv != NS_PROPTABLE_PROP_OVERWRITTEN,
1334 : "Setting override value when it's already set...?");
1335 :
1336 0 : if (NS_LIKELY(NS_SUCCEEDED(rv))) {
1337 0 : mImageNeedsTransformInvalidation = true;
1338 : } else {
1339 : // property-insertion failed (e.g. OOM in property-table code)
1340 : delete pAROverridePtr;
1341 : }
1342 : }
1343 :
1344 : void
1345 0 : nsSVGSVGElement::ClearImageOverridePreserveAspectRatio()
1346 : {
1347 : #ifdef DEBUG
1348 0 : NS_ABORT_IF_FALSE(GetCurrentDoc()->IsBeingUsedAsImage(),
1349 : "should only override preserveAspectRatio in images");
1350 : #endif
1351 :
1352 0 : mIsPaintingSVGImageElement = false;
1353 0 : if (!HasValidViewbox() && ShouldSynthesizeViewBox()) {
1354 : // My non-<svg:image> clients will want to paint me with a synthesized
1355 : // viewBox, but my <svg:image> client that just painted me did NOT
1356 : // use that. Need to tell ourselves to flush our transform.
1357 0 : mImageNeedsTransformInvalidation = true;
1358 : }
1359 :
1360 0 : void* valPtr = UnsetProperty(nsGkAtoms::overridePreserveAspectRatio);
1361 0 : if (valPtr) {
1362 0 : mImageNeedsTransformInvalidation = true;
1363 : delete static_cast<SVGPreserveAspectRatio*>(valPtr);
1364 : }
1365 0 : }
1366 :
1367 : const SVGPreserveAspectRatio*
1368 0 : nsSVGSVGElement::GetImageOverridePreserveAspectRatio() const
1369 : {
1370 0 : void* valPtr = GetProperty(nsGkAtoms::overridePreserveAspectRatio);
1371 : #ifdef DEBUG
1372 0 : if (valPtr) {
1373 0 : NS_ABORT_IF_FALSE(GetCurrentDoc()->IsBeingUsedAsImage(),
1374 : "should only override preserveAspectRatio in images");
1375 : }
1376 : #endif
1377 :
1378 0 : return static_cast<SVGPreserveAspectRatio*>(valPtr);
1379 : }
1380 :
1381 : void
1382 0 : nsSVGSVGElement::FlushImageTransformInvalidation()
1383 : {
1384 0 : NS_ABORT_IF_FALSE(!GetParent(), "Should only be called on root node");
1385 0 : NS_ABORT_IF_FALSE(GetCurrentDoc()->IsBeingUsedAsImage(),
1386 : "Should only be called on image documents");
1387 :
1388 0 : if (mImageNeedsTransformInvalidation) {
1389 0 : InvalidateTransformNotifyFrame();
1390 0 : mImageNeedsTransformInvalidation = false;
1391 : }
1392 4392 : }
|