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 : *
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.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsSVGOuterSVGFrame.h"
40 : #include "nsIDOMSVGSVGElement.h"
41 : #include "nsSVGSVGElement.h"
42 : #include "nsSVGTextFrame.h"
43 : #include "nsSVGForeignObjectFrame.h"
44 : #include "DOMSVGTests.h"
45 : #include "nsDisplayList.h"
46 : #include "nsStubMutationObserver.h"
47 : #include "gfxContext.h"
48 : #include "gfxMatrix.h"
49 : #include "gfxRect.h"
50 : #include "nsIContentViewer.h"
51 : #include "nsIDocShell.h"
52 : #include "nsIDOMDocument.h"
53 : #include "nsIDOMWindow.h"
54 : #include "nsPIDOMWindow.h"
55 : #include "nsIObjectLoadingContent.h"
56 : #include "nsIInterfaceRequestorUtils.h"
57 :
58 : namespace dom = mozilla::dom;
59 :
60 : class nsSVGMutationObserver : public nsStubMutationObserver
61 1464 : {
62 : public:
63 : // nsIMutationObserver interface
64 : NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
65 :
66 : // nsISupports interface:
67 : NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
68 : private:
69 0 : NS_IMETHOD_(nsrefcnt) AddRef() { return 1; }
70 0 : NS_IMETHOD_(nsrefcnt) Release() { return 1; }
71 :
72 : static void UpdateTextFragmentTrees(nsIFrame *aFrame);
73 : };
74 :
75 : //----------------------------------------------------------------------
76 : // nsISupports methods
77 :
78 0 : NS_INTERFACE_MAP_BEGIN(nsSVGMutationObserver)
79 0 : NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
80 0 : NS_INTERFACE_MAP_END
81 :
82 1464 : static nsSVGMutationObserver sSVGMutationObserver;
83 :
84 : //----------------------------------------------------------------------
85 : // nsIMutationObserver methods
86 :
87 : void
88 0 : nsSVGMutationObserver::AttributeChanged(nsIDocument* aDocument,
89 : dom::Element* aElement,
90 : PRInt32 aNameSpaceID,
91 : nsIAtom* aAttribute,
92 : PRInt32 aModType)
93 : {
94 0 : if (aNameSpaceID != kNameSpaceID_XML || aAttribute != nsGkAtoms::space) {
95 0 : return;
96 : }
97 :
98 0 : nsIFrame* frame = aElement->GetPrimaryFrame();
99 0 : if (!frame) {
100 0 : return;
101 : }
102 :
103 : // is the content a child of a text element
104 0 : nsSVGTextContainerFrame* containerFrame = do_QueryFrame(frame);
105 0 : if (containerFrame) {
106 0 : containerFrame->NotifyGlyphMetricsChange();
107 0 : return;
108 : }
109 : // if not, are there text elements amongst its descendents
110 0 : UpdateTextFragmentTrees(frame);
111 : }
112 :
113 : //----------------------------------------------------------------------
114 : // Implementation helpers
115 :
116 : void
117 0 : nsSVGMutationObserver::UpdateTextFragmentTrees(nsIFrame *aFrame)
118 : {
119 0 : nsIFrame* kid = aFrame->GetFirstPrincipalChild();
120 0 : while (kid) {
121 0 : if (kid->GetType() == nsGkAtoms::svgTextFrame) {
122 0 : nsSVGTextFrame* textFrame = static_cast<nsSVGTextFrame*>(kid);
123 0 : textFrame->NotifyGlyphMetricsChange();
124 : } else {
125 0 : UpdateTextFragmentTrees(kid);
126 : }
127 0 : kid = kid->GetNextSibling();
128 : }
129 0 : }
130 :
131 : //----------------------------------------------------------------------
132 : // Implementation
133 :
134 : nsIFrame*
135 0 : NS_NewSVGOuterSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
136 : {
137 0 : return new (aPresShell) nsSVGOuterSVGFrame(aContext);
138 : }
139 :
140 0 : NS_IMPL_FRAMEARENA_HELPERS(nsSVGOuterSVGFrame)
141 :
142 0 : nsSVGOuterSVGFrame::nsSVGOuterSVGFrame(nsStyleContext* aContext)
143 : : nsSVGOuterSVGFrameBase(aContext)
144 : , mRedrawSuspendCount(0)
145 : , mFullZoom(0)
146 : , mViewportInitialized(false)
147 : #ifdef XP_MACOSX
148 : , mEnableBitmapFallback(false)
149 : #endif
150 0 : , mIsRootContent(false)
151 : {
152 0 : }
153 :
154 : NS_IMETHODIMP
155 0 : nsSVGOuterSVGFrame::Init(nsIContent* aContent,
156 : nsIFrame* aParent,
157 : nsIFrame* aPrevInFlow)
158 : {
159 : #ifdef DEBUG
160 0 : nsCOMPtr<nsIDOMSVGSVGElement> svgElement = do_QueryInterface(aContent);
161 0 : NS_ASSERTION(svgElement, "Content is not an SVG 'svg' element!");
162 : #endif
163 :
164 0 : AddStateBits(NS_STATE_IS_OUTER_SVG);
165 :
166 : // Check for conditional processing attributes here rather than in
167 : // nsCSSFrameConstructor::FindSVGData because we want to avoid
168 : // simply giving failing outer <svg> elements an nsSVGContainerFrame.
169 0 : nsSVGSVGElement *svg = static_cast<nsSVGSVGElement*>(aContent);
170 0 : if (!svg->PassesConditionalProcessingTests()) {
171 0 : AddStateBits(NS_STATE_SVG_NONDISPLAY_CHILD);
172 : }
173 :
174 0 : nsresult rv = nsSVGOuterSVGFrameBase::Init(aContent, aParent, aPrevInFlow);
175 :
176 0 : nsIDocument* doc = mContent->GetCurrentDoc();
177 0 : if (doc) {
178 : // we only care about our content's zoom and pan values if it's the root element
179 0 : if (doc->GetRootElement() == mContent) {
180 0 : mIsRootContent = true;
181 : }
182 : // sSVGMutationObserver has the same lifetime as the document so does
183 : // not need to be removed
184 0 : doc->AddMutationObserverUnlessExists(&sSVGMutationObserver);
185 : }
186 :
187 0 : SuspendRedraw(); // UnsuspendRedraw is in DidReflow
188 :
189 0 : return rv;
190 : }
191 :
192 : //----------------------------------------------------------------------
193 : // nsQueryFrame methods
194 :
195 0 : NS_QUERYFRAME_HEAD(nsSVGOuterSVGFrame)
196 0 : NS_QUERYFRAME_ENTRY(nsISVGSVGFrame)
197 0 : NS_QUERYFRAME_TAIL_INHERITING(nsSVGOuterSVGFrameBase)
198 :
199 : //----------------------------------------------------------------------
200 : // nsIFrame methods
201 :
202 : //----------------------------------------------------------------------
203 : // reflowing
204 :
205 : /* virtual */ nscoord
206 0 : nsSVGOuterSVGFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
207 : {
208 : nscoord result;
209 0 : DISPLAY_MIN_WIDTH(this, result);
210 :
211 0 : result = nscoord(0);
212 :
213 0 : return result;
214 : }
215 :
216 : /* virtual */ nscoord
217 0 : nsSVGOuterSVGFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
218 : {
219 : nscoord result;
220 0 : DISPLAY_PREF_WIDTH(this, result);
221 :
222 0 : nsSVGSVGElement *svg = static_cast<nsSVGSVGElement*>(mContent);
223 0 : nsSVGLength2 &width = svg->mLengthAttributes[nsSVGSVGElement::WIDTH];
224 :
225 0 : if (width.IsPercentage()) {
226 : // It looks like our containing block's width may depend on our width. In
227 : // that case our behavior is undefined according to CSS 2.1 section 10.3.2,
228 : // so return zero.
229 0 : result = nscoord(0);
230 : } else {
231 0 : result = nsPresContext::CSSPixelsToAppUnits(width.GetAnimValue(svg));
232 0 : if (result < 0) {
233 0 : result = nscoord(0);
234 : }
235 : }
236 :
237 0 : return result;
238 : }
239 :
240 : /* virtual */ nsIFrame::IntrinsicSize
241 0 : nsSVGOuterSVGFrame::GetIntrinsicSize()
242 : {
243 : // XXXjwatt Note that here we want to return the CSS width/height if they're
244 : // specified and we're embedded inside an nsIObjectLoadingContent.
245 :
246 0 : IntrinsicSize intrinsicSize;
247 :
248 0 : nsSVGSVGElement *content = static_cast<nsSVGSVGElement*>(mContent);
249 0 : nsSVGLength2 &width = content->mLengthAttributes[nsSVGSVGElement::WIDTH];
250 0 : nsSVGLength2 &height = content->mLengthAttributes[nsSVGSVGElement::HEIGHT];
251 :
252 0 : if (!width.IsPercentage()) {
253 0 : nscoord val = nsPresContext::CSSPixelsToAppUnits(width.GetAnimValue(content));
254 0 : if (val < 0) val = 0;
255 0 : intrinsicSize.width.SetCoordValue(val);
256 : }
257 :
258 0 : if (!height.IsPercentage()) {
259 0 : nscoord val = nsPresContext::CSSPixelsToAppUnits(height.GetAnimValue(content));
260 0 : if (val < 0) val = 0;
261 0 : intrinsicSize.height.SetCoordValue(val);
262 : }
263 :
264 : return intrinsicSize;
265 : }
266 :
267 : /* virtual */ nsSize
268 0 : nsSVGOuterSVGFrame::GetIntrinsicRatio()
269 : {
270 : // We only have an intrinsic size/ratio if our width and height attributes
271 : // are both specified and set to non-percentage values, or we have a viewBox
272 : // rect: http://www.w3.org/TR/SVGMobile12/coords.html#IntrinsicSizing
273 :
274 0 : nsSVGSVGElement *content = static_cast<nsSVGSVGElement*>(mContent);
275 0 : nsSVGLength2 &width = content->mLengthAttributes[nsSVGSVGElement::WIDTH];
276 0 : nsSVGLength2 &height = content->mLengthAttributes[nsSVGSVGElement::HEIGHT];
277 :
278 0 : if (!width.IsPercentage() && !height.IsPercentage()) {
279 : nsSize ratio(NSToCoordRoundWithClamp(width.GetAnimValue(content)),
280 0 : NSToCoordRoundWithClamp(height.GetAnimValue(content)));
281 0 : if (ratio.width < 0) {
282 0 : ratio.width = 0;
283 : }
284 0 : if (ratio.height < 0) {
285 0 : ratio.height = 0;
286 : }
287 0 : return ratio;
288 : }
289 :
290 0 : if (content->mViewBox.IsValid()) {
291 0 : const nsSVGViewBoxRect viewbox = content->mViewBox.GetAnimValue();
292 0 : float viewBoxWidth = viewbox.width;
293 0 : float viewBoxHeight = viewbox.height;
294 :
295 0 : if (viewBoxWidth < 0.0f) {
296 0 : viewBoxWidth = 0.0f;
297 : }
298 0 : if (viewBoxHeight < 0.0f) {
299 0 : viewBoxHeight = 0.0f;
300 : }
301 : return nsSize(NSToCoordRoundWithClamp(viewBoxWidth),
302 0 : NSToCoordRoundWithClamp(viewBoxHeight));
303 : }
304 :
305 0 : return nsSVGOuterSVGFrameBase::GetIntrinsicRatio();
306 : }
307 :
308 : /* virtual */ nsSize
309 0 : nsSVGOuterSVGFrame::ComputeSize(nsRenderingContext *aRenderingContext,
310 : nsSize aCBSize, nscoord aAvailableWidth,
311 : nsSize aMargin, nsSize aBorder, nsSize aPadding,
312 : bool aShrinkWrap)
313 : {
314 0 : nsSVGSVGElement* content = static_cast<nsSVGSVGElement*>(mContent);
315 :
316 0 : IntrinsicSize intrinsicSize = GetIntrinsicSize();
317 :
318 0 : if (!mContent->GetParent()) {
319 0 : if (IsRootOfImage() || IsRootOfReplacedElementSubDoc()) {
320 : // The embedding element has done the replaced element sizing,
321 : // using our intrinsic dimensions as necessary. We just need to
322 : // fill the viewport.
323 0 : return aCBSize;
324 : } else {
325 : // We're the root of a browsing context, so we need to honor
326 : // widths and heights in percentages. (GetIntrinsicSize() doesn't
327 : // report these since there's no such thing as a percentage
328 : // intrinsic size.)
329 : nsSVGLength2 &width =
330 0 : content->mLengthAttributes[nsSVGSVGElement::WIDTH];
331 0 : if (width.IsPercentage()) {
332 0 : NS_ABORT_IF_FALSE(intrinsicSize.width.GetUnit() == eStyleUnit_None,
333 : "GetIntrinsicSize should have reported no "
334 : "intrinsic width");
335 0 : float val = width.GetAnimValInSpecifiedUnits() / 100.0f;
336 0 : if (val < 0.0f) val = 0.0f;
337 0 : intrinsicSize.width.SetCoordValue(val * aCBSize.width);
338 : }
339 :
340 : nsSVGLength2 &height =
341 0 : content->mLengthAttributes[nsSVGSVGElement::HEIGHT];
342 0 : NS_ASSERTION(aCBSize.height != NS_AUTOHEIGHT,
343 : "root should not have auto-height containing block");
344 0 : if (height.IsPercentage()) {
345 0 : NS_ABORT_IF_FALSE(intrinsicSize.height.GetUnit() == eStyleUnit_None,
346 : "GetIntrinsicSize should have reported no "
347 : "intrinsic height");
348 0 : float val = height.GetAnimValInSpecifiedUnits() / 100.0f;
349 0 : if (val < 0.0f) val = 0.0f;
350 0 : intrinsicSize.height.SetCoordValue(val * aCBSize.height);
351 : }
352 0 : NS_ABORT_IF_FALSE(intrinsicSize.height.GetUnit() == eStyleUnit_Coord &&
353 : intrinsicSize.width.GetUnit() == eStyleUnit_Coord,
354 : "We should have just handled the only situation where"
355 : "we lack an intrinsic height or width.");
356 : }
357 : }
358 :
359 : return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(
360 : aRenderingContext, this,
361 0 : intrinsicSize, GetIntrinsicRatio(), aCBSize,
362 0 : aMargin, aBorder, aPadding);
363 : }
364 :
365 : NS_IMETHODIMP
366 0 : nsSVGOuterSVGFrame::Reflow(nsPresContext* aPresContext,
367 : nsHTMLReflowMetrics& aDesiredSize,
368 : const nsHTMLReflowState& aReflowState,
369 : nsReflowStatus& aStatus)
370 : {
371 0 : DO_GLOBAL_REFLOW_COUNT("nsSVGOuterSVGFrame");
372 0 : DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
373 0 : NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
374 : ("enter nsSVGOuterSVGFrame::Reflow: availSize=%d,%d",
375 : aReflowState.availableWidth, aReflowState.availableHeight));
376 :
377 0 : NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
378 :
379 0 : aStatus = NS_FRAME_COMPLETE;
380 :
381 0 : aDesiredSize.width = aReflowState.ComputedWidth() +
382 0 : aReflowState.mComputedBorderPadding.LeftRight();
383 0 : aDesiredSize.height = aReflowState.ComputedHeight() +
384 0 : aReflowState.mComputedBorderPadding.TopBottom();
385 :
386 0 : NS_ASSERTION(!GetPrevInFlow(), "SVG can't currently be broken across pages.");
387 :
388 : // Make sure we scroll if we're too big:
389 : // XXX Use the bounding box of our descendants? (See bug 353460 comment 14.)
390 0 : aDesiredSize.SetOverflowAreasToDesiredBounds();
391 0 : FinishAndStoreOverflow(&aDesiredSize);
392 :
393 : // If our SVG viewport has changed, update our content and notify.
394 : // http://www.w3.org/TR/SVG11/coords.html#ViewportSpace
395 :
396 : svgFloatSize newViewportSize(
397 : nsPresContext::AppUnitsToFloatCSSPixels(aReflowState.ComputedWidth()),
398 0 : nsPresContext::AppUnitsToFloatCSSPixels(aReflowState.ComputedHeight()));
399 :
400 0 : nsSVGSVGElement *svgElem = static_cast<nsSVGSVGElement*>(mContent);
401 :
402 0 : if (newViewportSize != svgElem->GetViewportSize() ||
403 0 : mFullZoom != PresContext()->GetFullZoom()) {
404 0 : svgElem->SetViewportSize(newViewportSize);
405 0 : mViewportInitialized = true;
406 0 : mFullZoom = PresContext()->GetFullZoom();
407 0 : NotifyViewportChange();
408 : }
409 :
410 0 : NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
411 : ("exit nsSVGOuterSVGFrame::Reflow: size=%d,%d",
412 : aDesiredSize.width, aDesiredSize.height));
413 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
414 0 : return NS_OK;
415 : }
416 :
417 : static PLDHashOperator
418 0 : ReflowForeignObject(nsVoidPtrHashKey *aEntry, void* aUserArg)
419 : {
420 : static_cast<nsSVGForeignObjectFrame*>
421 0 : (const_cast<void*>(aEntry->GetKey()))->MaybeReflowFromOuterSVGFrame();
422 0 : return PL_DHASH_NEXT;
423 : }
424 :
425 : NS_IMETHODIMP
426 0 : nsSVGOuterSVGFrame::DidReflow(nsPresContext* aPresContext,
427 : const nsHTMLReflowState* aReflowState,
428 : nsDidReflowStatus aStatus)
429 : {
430 0 : bool firstReflow = (GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
431 :
432 0 : nsresult rv = nsSVGOuterSVGFrameBase::DidReflow(aPresContext,aReflowState,aStatus);
433 :
434 0 : if (firstReflow) {
435 : // call InitialUpdate() on all frames:
436 0 : nsIFrame* kid = mFrames.FirstChild();
437 0 : while (kid) {
438 0 : nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
439 0 : if (SVGFrame) {
440 0 : SVGFrame->InitialUpdate();
441 : }
442 0 : kid = kid->GetNextSibling();
443 : }
444 :
445 0 : UnsuspendRedraw(); // For the SuspendRedraw in InitSVG
446 : } else {
447 : // Now that all viewport establishing descendants have their correct size,
448 : // tell our foreignObject descendants to reflow their children.
449 0 : if (mForeignObjectHash.IsInitialized()) {
450 : #ifdef DEBUG
451 : PRUint32 count =
452 : #endif
453 0 : mForeignObjectHash.EnumerateEntries(ReflowForeignObject, nsnull);
454 0 : NS_ASSERTION(count == mForeignObjectHash.Count(),
455 : "We didn't reflow all our nsSVGForeignObjectFrames!");
456 : }
457 : }
458 :
459 0 : return rv;
460 : }
461 :
462 : //----------------------------------------------------------------------
463 : // container methods
464 :
465 : class nsDisplaySVG : public nsDisplayItem {
466 : public:
467 0 : nsDisplaySVG(nsDisplayListBuilder* aBuilder,
468 : nsSVGOuterSVGFrame* aFrame) :
469 0 : nsDisplayItem(aBuilder, aFrame) {
470 0 : MOZ_COUNT_CTOR(nsDisplaySVG);
471 0 : }
472 : #ifdef NS_BUILD_REFCNT_LOGGING
473 0 : virtual ~nsDisplaySVG() {
474 0 : MOZ_COUNT_DTOR(nsDisplaySVG);
475 0 : }
476 : #endif
477 :
478 : virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
479 : HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames);
480 : virtual void Paint(nsDisplayListBuilder* aBuilder,
481 : nsRenderingContext* aCtx);
482 0 : NS_DISPLAY_DECL_NAME("SVGEventReceiver", TYPE_SVG_EVENT_RECEIVER)
483 : };
484 :
485 : void
486 0 : nsDisplaySVG::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
487 : HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
488 : {
489 0 : nsSVGOuterSVGFrame *outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(mFrame);
490 0 : nsRect rectAtOrigin = aRect - ToReferenceFrame();
491 0 : nsRect thisRect(nsPoint(0,0), outerSVGFrame->GetSize());
492 0 : if (!thisRect.Intersects(rectAtOrigin))
493 : return;
494 :
495 : nsPoint rectCenter(rectAtOrigin.x + rectAtOrigin.width / 2,
496 0 : rectAtOrigin.y + rectAtOrigin.height / 2);
497 :
498 : nsIFrame* frame = nsSVGUtils::HitTestChildren(
499 : outerSVGFrame, rectCenter + outerSVGFrame->GetPosition() -
500 0 : outerSVGFrame->GetContentRect().TopLeft());
501 0 : if (frame) {
502 0 : aOutFrames->AppendElement(frame);
503 : }
504 : }
505 :
506 : void
507 0 : nsDisplaySVG::Paint(nsDisplayListBuilder* aBuilder,
508 : nsRenderingContext* aContext)
509 : {
510 0 : nsSVGOuterSVGFrame *frame = static_cast<nsSVGOuterSVGFrame*>(mFrame);
511 :
512 0 : if (frame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)
513 0 : return;
514 :
515 : #if defined(DEBUG) && defined(SVG_DEBUG_PAINT_TIMING)
516 : PRTime start = PR_Now();
517 : #endif
518 :
519 0 : aContext->PushState();
520 :
521 : #ifdef XP_MACOSX
522 : if (frame->BitmapFallbackEnabled()) {
523 : // nquartz fallback paths, which svg tends to trigger, need
524 : // a non-window context target
525 : aContext->ThebesContext()->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
526 : }
527 : #endif
528 :
529 0 : frame->Paint(aBuilder, aContext, mVisibleRect, ToReferenceFrame());
530 :
531 : #ifdef XP_MACOSX
532 : if (frame->BitmapFallbackEnabled()) {
533 : // show the surface we pushed earlier for fallbacks
534 : aContext->ThebesContext()->PopGroupToSource();
535 : aContext->ThebesContext()->Paint();
536 : }
537 :
538 : if (aContext->ThebesContext()->HasError() && !frame->BitmapFallbackEnabled()) {
539 : frame->SetBitmapFallbackEnabled(true);
540 : // It's not really clear what area to invalidate here. We might have
541 : // stuffed up rendering for the entire window in this paint pass,
542 : // so we can't just invalidate our own rect. Invalidate everything
543 : // in sight.
544 : // This won't work for printing, by the way, but failure to print the
545 : // odd document is probably no worse than printing horribly for all
546 : // documents. Better to fix things so we don't need fallback.
547 : nsIFrame* ancestor = frame;
548 : PRUint32 flags = 0;
549 : while (true) {
550 : nsIFrame* next = nsLayoutUtils::GetCrossDocParentFrame(ancestor);
551 : if (!next)
552 : break;
553 : if (ancestor->GetParent() != next) {
554 : // We're crossing a document boundary. Logically, the invalidation is
555 : // being triggered by a subdocument of the root document. This will
556 : // prevent an untrusted root document being told about invalidation
557 : // that happened because a child was using SVG...
558 : flags |= nsIFrame::INVALIDATE_CROSS_DOC;
559 : }
560 : ancestor = next;
561 : }
562 : ancestor->InvalidateWithFlags(nsRect(nsPoint(0, 0), ancestor->GetSize()), flags);
563 : }
564 : #endif
565 :
566 0 : aContext->PopState();
567 :
568 : #if defined(DEBUG) && defined(SVG_DEBUG_PAINT_TIMING)
569 : PRTime end = PR_Now();
570 : printf("SVG Paint Timing: %f ms\n", (end-start)/1000.0);
571 : #endif
572 : }
573 :
574 : // helper
575 : static inline bool
576 0 : DependsOnIntrinsicSize(const nsIFrame* aEmbeddingFrame)
577 : {
578 0 : const nsStylePosition *pos = aEmbeddingFrame->GetStylePosition();
579 0 : const nsStyleCoord &width = pos->mWidth;
580 0 : const nsStyleCoord &height = pos->mHeight;
581 :
582 : // XXX it would be nice to know if the size of aEmbeddingFrame's containing
583 : // block depends on aEmbeddingFrame, then we'd know if we can return false
584 : // for eStyleUnit_Percent too.
585 0 : return !width.ConvertsToLength() ||
586 0 : !height.ConvertsToLength();
587 : }
588 :
589 : NS_IMETHODIMP
590 0 : nsSVGOuterSVGFrame::AttributeChanged(PRInt32 aNameSpaceID,
591 : nsIAtom* aAttribute,
592 : PRInt32 aModType)
593 : {
594 0 : if (aNameSpaceID == kNameSpaceID_None &&
595 0 : !(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
596 0 : if (aAttribute == nsGkAtoms::viewBox ||
597 : aAttribute == nsGkAtoms::preserveAspectRatio ||
598 : aAttribute == nsGkAtoms::transform) {
599 :
600 : // make sure our cached transform matrix gets (lazily) updated
601 0 : mCanvasTM = nsnull;
602 :
603 : nsSVGUtils::NotifyChildrenOfSVGChange(
604 : this, aAttribute == nsGkAtoms::viewBox ?
605 0 : TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED : TRANSFORM_CHANGED);
606 :
607 0 : } else if (aAttribute == nsGkAtoms::width ||
608 : aAttribute == nsGkAtoms::height) {
609 :
610 : nsIFrame* embeddingFrame;
611 0 : if (IsRootOfReplacedElementSubDoc(&embeddingFrame) && embeddingFrame) {
612 0 : if (DependsOnIntrinsicSize(embeddingFrame)) {
613 : // Tell embeddingFrame's presShell it needs to be reflowed (which takes
614 : // care of reflowing us too).
615 0 : embeddingFrame->PresContext()->PresShell()->
616 0 : FrameNeedsReflow(embeddingFrame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
617 : }
618 : // else our width and height is overridden - don't reflow anything
619 : } else {
620 : // We are not embedded by reference, so our 'width' and 'height'
621 : // attributes are not overridden - we need to reflow.
622 0 : PresContext()->PresShell()->
623 0 : FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
624 : }
625 : }
626 : }
627 :
628 0 : return NS_OK;
629 : }
630 :
631 : //----------------------------------------------------------------------
632 : // painting
633 :
634 : NS_IMETHODIMP
635 0 : nsSVGOuterSVGFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
636 : const nsRect& aDirtyRect,
637 : const nsDisplayListSet& aLists)
638 : {
639 0 : nsresult rv = DisplayBorderBackgroundOutline(aBuilder, aLists);
640 0 : NS_ENSURE_SUCCESS(rv, rv);
641 :
642 : return aLists.Content()->AppendNewToTop(
643 0 : new (aBuilder) nsDisplaySVG(aBuilder, this));
644 : }
645 :
646 : void
647 0 : nsSVGOuterSVGFrame::Paint(const nsDisplayListBuilder* aBuilder,
648 : nsRenderingContext* aContext,
649 : const nsRect& aDirtyRect, nsPoint aPt)
650 : {
651 0 : nsRect viewportRect = GetContentRect();
652 0 : nsPoint viewportOffset = aPt + viewportRect.TopLeft() - GetPosition();
653 0 : viewportRect.MoveTo(viewportOffset);
654 :
655 0 : nsRect clipRect;
656 0 : clipRect.IntersectRect(aDirtyRect, viewportRect);
657 0 : aContext->IntersectClip(clipRect);
658 0 : aContext->Translate(viewportRect.TopLeft());
659 0 : nsRect dirtyRect = clipRect - viewportOffset;
660 :
661 0 : nsIntRect dirtyPxRect = dirtyRect.ToOutsidePixels(PresContext()->AppUnitsPerDevPixel());
662 :
663 : // Create an SVGAutoRenderState so we can call SetPaintingToWindow on
664 : // it, but don't change the render mode:
665 0 : SVGAutoRenderState state(aContext, SVGAutoRenderState::GetRenderMode(aContext));
666 :
667 0 : if (aBuilder->IsPaintingToWindow()) {
668 0 : state.SetPaintingToWindow(true);
669 : }
670 :
671 0 : nsSVGUtils::PaintFrameWithEffects(aContext, &dirtyPxRect, this);
672 0 : }
673 :
674 : nsSplittableType
675 0 : nsSVGOuterSVGFrame::GetSplittableType() const
676 : {
677 0 : return NS_FRAME_NOT_SPLITTABLE;
678 : }
679 :
680 : nsIAtom *
681 0 : nsSVGOuterSVGFrame::GetType() const
682 : {
683 0 : return nsGkAtoms::svgOuterSVGFrame;
684 : }
685 :
686 : //----------------------------------------------------------------------
687 : // nsISVGSVGFrame methods:
688 :
689 : void
690 0 : nsSVGOuterSVGFrame::SuspendRedraw()
691 : {
692 0 : if (++mRedrawSuspendCount != 1)
693 0 : return;
694 :
695 0 : nsSVGUtils::NotifyRedrawSuspended(this);
696 : }
697 :
698 : void
699 0 : nsSVGOuterSVGFrame::UnsuspendRedraw()
700 : {
701 : NS_ASSERTION(mRedrawSuspendCount >=0, "unbalanced suspend count!");
702 :
703 0 : if (--mRedrawSuspendCount > 0)
704 0 : return;
705 :
706 0 : nsSVGUtils::NotifyRedrawUnsuspended(this);
707 : }
708 :
709 : void
710 0 : nsSVGOuterSVGFrame::NotifyViewportChange()
711 : {
712 : // no point in doing anything when were not init'ed yet:
713 0 : if (!mViewportInitialized) {
714 0 : return;
715 : }
716 :
717 0 : PRUint32 flags = COORD_CONTEXT_CHANGED;
718 :
719 : // viewport changes only affect our transform if we have a viewBox attribute
720 : #if 1
721 : {
722 : #else
723 : // XXX this caused reftest failures (bug 413960)
724 : if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::viewBox)) {
725 : #endif
726 : // make sure canvas transform matrix gets (lazily) recalculated:
727 0 : mCanvasTM = nsnull;
728 :
729 0 : flags |= TRANSFORM_CHANGED;
730 : }
731 :
732 : // inform children
733 0 : nsSVGUtils::NotifyChildrenOfSVGChange(this, flags);
734 : }
735 :
736 : //----------------------------------------------------------------------
737 : // nsSVGContainerFrame methods:
738 :
739 : gfxMatrix
740 0 : nsSVGOuterSVGFrame::GetCanvasTM()
741 : {
742 0 : if (!mCanvasTM) {
743 0 : nsSVGSVGElement *content = static_cast<nsSVGSVGElement*>(mContent);
744 :
745 : float devPxPerCSSPx =
746 0 : 1.0f / PresContext()->AppUnitsToFloatCSSPixels(
747 0 : PresContext()->AppUnitsPerDevPixel());
748 :
749 0 : gfxMatrix viewBoxTM = content->GetViewBoxTransform();
750 :
751 0 : gfxMatrix zoomPanTM;
752 0 : if (mIsRootContent) {
753 0 : const nsSVGTranslatePoint& translate = content->GetCurrentTranslate();
754 0 : zoomPanTM.Translate(gfxPoint(translate.GetX(), translate.GetY()));
755 0 : zoomPanTM.Scale(content->GetCurrentScale(), content->GetCurrentScale());
756 : }
757 :
758 0 : gfxMatrix TM = viewBoxTM * zoomPanTM * gfxMatrix().Scale(devPxPerCSSPx, devPxPerCSSPx);
759 0 : mCanvasTM = new gfxMatrix(TM);
760 : }
761 0 : return *mCanvasTM;
762 : }
763 :
764 : //----------------------------------------------------------------------
765 : // Implementation helpers
766 :
767 : void
768 0 : nsSVGOuterSVGFrame::RegisterForeignObject(nsSVGForeignObjectFrame* aFrame)
769 : {
770 0 : NS_ASSERTION(aFrame, "Who on earth is calling us?!");
771 :
772 0 : if (!mForeignObjectHash.IsInitialized()) {
773 0 : if (!mForeignObjectHash.Init()) {
774 0 : NS_ERROR("Failed to initialize foreignObject hash.");
775 0 : return;
776 : }
777 : }
778 :
779 0 : NS_ASSERTION(!mForeignObjectHash.GetEntry(aFrame),
780 : "nsSVGForeignObjectFrame already registered!");
781 :
782 0 : mForeignObjectHash.PutEntry(aFrame);
783 :
784 0 : NS_ASSERTION(mForeignObjectHash.GetEntry(aFrame),
785 : "Failed to register nsSVGForeignObjectFrame!");
786 : }
787 :
788 : void
789 0 : nsSVGOuterSVGFrame::UnregisterForeignObject(nsSVGForeignObjectFrame* aFrame)
790 : {
791 0 : NS_ASSERTION(aFrame, "Who on earth is calling us?!");
792 0 : NS_ASSERTION(mForeignObjectHash.GetEntry(aFrame),
793 : "nsSVGForeignObjectFrame not in registry!");
794 0 : return mForeignObjectHash.RemoveEntry(aFrame);
795 : }
796 :
797 : bool
798 0 : nsSVGOuterSVGFrame::IsRootOfReplacedElementSubDoc(nsIFrame **aEmbeddingFrame)
799 : {
800 0 : if (!mContent->GetParent()) {
801 : // Our content is the document element
802 0 : nsCOMPtr<nsISupports> container = PresContext()->GetContainer();
803 0 : nsCOMPtr<nsIDOMWindow> window = do_GetInterface(container);
804 0 : if (window) {
805 0 : nsCOMPtr<nsIDOMElement> frameElement;
806 0 : window->GetFrameElement(getter_AddRefs(frameElement));
807 0 : nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(frameElement);
808 0 : if (olc) {
809 : // Our document is inside an HTML 'object', 'embed' or 'applet' element
810 0 : if (aEmbeddingFrame) {
811 0 : nsCOMPtr<nsIContent> element = do_QueryInterface(frameElement);
812 : *aEmbeddingFrame =
813 0 : static_cast<nsGenericElement*>(element.get())->GetPrimaryFrame();
814 0 : NS_ASSERTION(*aEmbeddingFrame, "Yikes, no embedding frame!");
815 : }
816 0 : return true;
817 : }
818 : }
819 : }
820 0 : if (aEmbeddingFrame) {
821 0 : *aEmbeddingFrame = nsnull;
822 : }
823 0 : return false;
824 : }
825 :
826 : bool
827 0 : nsSVGOuterSVGFrame::IsRootOfImage()
828 : {
829 0 : if (!mContent->GetParent()) {
830 : // Our content is the document element
831 0 : nsIDocument* doc = mContent->GetCurrentDoc();
832 0 : if (doc && doc->IsBeingUsedAsImage()) {
833 : // Our document is being used as an image
834 0 : return true;
835 : }
836 : }
837 :
838 0 : return false;
839 4392 : }
|