Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 // Main header first:
7 #include "nsSVGOuterSVGFrame.h"
9 // Keep others in (case-insensitive) order:
10 #include "nsDisplayList.h"
11 #include "nsIDocument.h"
12 #include "nsIDOMWindow.h"
13 #include "nsIInterfaceRequestorUtils.h"
14 #include "nsIObjectLoadingContent.h"
15 #include "nsRenderingContext.h"
16 #include "nsSVGIntegrationUtils.h"
17 #include "nsSVGForeignObjectFrame.h"
18 #include "mozilla/dom/SVGSVGElement.h"
19 #include "mozilla/dom/SVGViewElement.h"
20 #include "nsSubDocumentFrame.h"
22 using namespace mozilla;
23 using namespace mozilla::dom;
25 //----------------------------------------------------------------------
26 // Implementation helpers
28 void
29 nsSVGOuterSVGFrame::RegisterForeignObject(nsSVGForeignObjectFrame* aFrame)
30 {
31 NS_ASSERTION(aFrame, "Who on earth is calling us?!");
33 if (!mForeignObjectHash) {
34 mForeignObjectHash = new nsTHashtable<nsPtrHashKey<nsSVGForeignObjectFrame> >();
35 }
37 NS_ASSERTION(!mForeignObjectHash->GetEntry(aFrame),
38 "nsSVGForeignObjectFrame already registered!");
40 mForeignObjectHash->PutEntry(aFrame);
42 NS_ASSERTION(mForeignObjectHash->GetEntry(aFrame),
43 "Failed to register nsSVGForeignObjectFrame!");
44 }
46 void
47 nsSVGOuterSVGFrame::UnregisterForeignObject(nsSVGForeignObjectFrame* aFrame)
48 {
49 NS_ASSERTION(aFrame, "Who on earth is calling us?!");
50 NS_ASSERTION(mForeignObjectHash && mForeignObjectHash->GetEntry(aFrame),
51 "nsSVGForeignObjectFrame not in registry!");
52 return mForeignObjectHash->RemoveEntry(aFrame);
53 }
55 //----------------------------------------------------------------------
56 // Implementation
58 nsIFrame*
59 NS_NewSVGOuterSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
60 {
61 return new (aPresShell) nsSVGOuterSVGFrame(aContext);
62 }
64 NS_IMPL_FRAMEARENA_HELPERS(nsSVGOuterSVGFrame)
66 nsSVGOuterSVGFrame::nsSVGOuterSVGFrame(nsStyleContext* aContext)
67 : nsSVGOuterSVGFrameBase(aContext)
68 , mFullZoom(aContext->PresContext()->GetFullZoom())
69 , mViewportInitialized(false)
70 , mIsRootContent(false)
71 {
72 // Outer-<svg> has CSS layout, so remove this bit:
73 RemoveStateBits(NS_FRAME_SVG_LAYOUT);
74 }
76 void
77 nsSVGOuterSVGFrame::Init(nsIContent* aContent,
78 nsIFrame* aParent,
79 nsIFrame* aPrevInFlow)
80 {
81 NS_ASSERTION(aContent->IsSVG(nsGkAtoms::svg),
82 "Content is not an SVG 'svg' element!");
84 AddStateBits(NS_STATE_IS_OUTER_SVG |
85 NS_FRAME_FONT_INFLATION_CONTAINER |
86 NS_FRAME_FONT_INFLATION_FLOW_ROOT);
88 // Check for conditional processing attributes here rather than in
89 // nsCSSFrameConstructor::FindSVGData because we want to avoid
90 // simply giving failing outer <svg> elements an nsSVGContainerFrame.
91 // We don't create other SVG frames if PassesConditionalProcessingTests
92 // returns false, but since we do create nsSVGOuterSVGFrame frames we
93 // prevent them from painting by [ab]use NS_FRAME_IS_NONDISPLAY. The
94 // frame will be recreated via an nsChangeHint_ReconstructFrame restyle if
95 // the value returned by PassesConditionalProcessingTests changes.
96 SVGSVGElement *svg = static_cast<SVGSVGElement*>(aContent);
97 if (!svg->PassesConditionalProcessingTests()) {
98 AddStateBits(NS_FRAME_IS_NONDISPLAY);
99 }
101 nsSVGOuterSVGFrameBase::Init(aContent, aParent, aPrevInFlow);
103 nsIDocument* doc = mContent->GetCurrentDoc();
104 if (doc) {
105 // we only care about our content's zoom and pan values if it's the root element
106 if (doc->GetRootElement() == mContent) {
107 mIsRootContent = true;
108 }
109 }
110 }
112 //----------------------------------------------------------------------
113 // nsQueryFrame methods
115 NS_QUERYFRAME_HEAD(nsSVGOuterSVGFrame)
116 NS_QUERYFRAME_ENTRY(nsISVGSVGFrame)
117 NS_QUERYFRAME_TAIL_INHERITING(nsSVGOuterSVGFrameBase)
119 //----------------------------------------------------------------------
120 // nsIFrame methods
122 //----------------------------------------------------------------------
123 // reflowing
125 /* virtual */ nscoord
126 nsSVGOuterSVGFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
127 {
128 nscoord result;
129 DISPLAY_MIN_WIDTH(this, result);
131 result = nscoord(0);
133 return result;
134 }
136 /* virtual */ nscoord
137 nsSVGOuterSVGFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
138 {
139 nscoord result;
140 DISPLAY_PREF_WIDTH(this, result);
142 SVGSVGElement *svg = static_cast<SVGSVGElement*>(mContent);
143 nsSVGLength2 &width = svg->mLengthAttributes[SVGSVGElement::ATTR_WIDTH];
145 if (width.IsPercentage()) {
146 // It looks like our containing block's width may depend on our width. In
147 // that case our behavior is undefined according to CSS 2.1 section 10.3.2,
148 // so return zero.
149 result = nscoord(0);
150 } else {
151 result = nsPresContext::CSSPixelsToAppUnits(width.GetAnimValue(svg));
152 if (result < 0) {
153 result = nscoord(0);
154 }
155 }
157 return result;
158 }
160 /* virtual */ IntrinsicSize
161 nsSVGOuterSVGFrame::GetIntrinsicSize()
162 {
163 // XXXjwatt Note that here we want to return the CSS width/height if they're
164 // specified and we're embedded inside an nsIObjectLoadingContent.
166 IntrinsicSize intrinsicSize;
168 SVGSVGElement *content = static_cast<SVGSVGElement*>(mContent);
169 nsSVGLength2 &width = content->mLengthAttributes[SVGSVGElement::ATTR_WIDTH];
170 nsSVGLength2 &height = content->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT];
172 if (!width.IsPercentage()) {
173 nscoord val = nsPresContext::CSSPixelsToAppUnits(width.GetAnimValue(content));
174 if (val < 0) val = 0;
175 intrinsicSize.width.SetCoordValue(val);
176 }
178 if (!height.IsPercentage()) {
179 nscoord val = nsPresContext::CSSPixelsToAppUnits(height.GetAnimValue(content));
180 if (val < 0) val = 0;
181 intrinsicSize.height.SetCoordValue(val);
182 }
184 return intrinsicSize;
185 }
187 /* virtual */ nsSize
188 nsSVGOuterSVGFrame::GetIntrinsicRatio()
189 {
190 // We only have an intrinsic size/ratio if our width and height attributes
191 // are both specified and set to non-percentage values, or we have a viewBox
192 // rect: http://www.w3.org/TR/SVGMobile12/coords.html#IntrinsicSizing
194 SVGSVGElement *content = static_cast<SVGSVGElement*>(mContent);
195 nsSVGLength2 &width = content->mLengthAttributes[SVGSVGElement::ATTR_WIDTH];
196 nsSVGLength2 &height = content->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT];
198 if (!width.IsPercentage() && !height.IsPercentage()) {
199 nsSize ratio(NSToCoordRoundWithClamp(width.GetAnimValue(content)),
200 NSToCoordRoundWithClamp(height.GetAnimValue(content)));
201 if (ratio.width < 0) {
202 ratio.width = 0;
203 }
204 if (ratio.height < 0) {
205 ratio.height = 0;
206 }
207 return ratio;
208 }
210 SVGViewElement* viewElement = content->GetCurrentViewElement();
211 const nsSVGViewBoxRect* viewbox = nullptr;
213 // The logic here should match HasViewBox().
214 if (viewElement && viewElement->mViewBox.HasRect()) {
215 viewbox = &viewElement->mViewBox.GetAnimValue();
216 } else if (content->mViewBox.HasRect()) {
217 viewbox = &content->mViewBox.GetAnimValue();
218 }
220 if (viewbox) {
221 float viewBoxWidth = viewbox->width;
222 float viewBoxHeight = viewbox->height;
224 if (viewBoxWidth < 0.0f) {
225 viewBoxWidth = 0.0f;
226 }
227 if (viewBoxHeight < 0.0f) {
228 viewBoxHeight = 0.0f;
229 }
230 return nsSize(NSToCoordRoundWithClamp(viewBoxWidth),
231 NSToCoordRoundWithClamp(viewBoxHeight));
232 }
234 return nsSVGOuterSVGFrameBase::GetIntrinsicRatio();
235 }
237 /* virtual */ nsSize
238 nsSVGOuterSVGFrame::ComputeSize(nsRenderingContext *aRenderingContext,
239 nsSize aCBSize, nscoord aAvailableWidth,
240 nsSize aMargin, nsSize aBorder, nsSize aPadding,
241 uint32_t aFlags)
242 {
243 if (IsRootOfImage() || IsRootOfReplacedElementSubDoc()) {
244 // The embedding element has sized itself using the CSS replaced element
245 // sizing rules, using our intrinsic dimensions as necessary. The SVG spec
246 // says that the width and height of embedded SVG is overridden by the
247 // width and height of the embedding element, so we just need to size to
248 // the viewport that the embedding element has established for us.
249 return aCBSize;
250 }
252 nsSize cbSize = aCBSize;
253 IntrinsicSize intrinsicSize = GetIntrinsicSize();
255 if (!mContent->GetParent()) {
256 // We're the root of the outermost browsing context, so we need to scale
257 // cbSize by the full-zoom so that SVGs with percentage width/height zoom:
259 NS_ASSERTION(aCBSize.width != NS_AUTOHEIGHT &&
260 aCBSize.height != NS_AUTOHEIGHT,
261 "root should not have auto-width/height containing block");
262 cbSize.width *= PresContext()->GetFullZoom();
263 cbSize.height *= PresContext()->GetFullZoom();
265 // We also need to honour the width and height attributes' default values
266 // of 100% when we're the root of a browsing context. (GetIntrinsicSize()
267 // doesn't report these since there's no such thing as a percentage
268 // intrinsic size. Also note that explicit percentage values are mapped
269 // into style, so the following isn't for them.)
271 SVGSVGElement* content = static_cast<SVGSVGElement*>(mContent);
273 nsSVGLength2 &width =
274 content->mLengthAttributes[SVGSVGElement::ATTR_WIDTH];
275 if (width.IsPercentage()) {
276 NS_ABORT_IF_FALSE(intrinsicSize.width.GetUnit() == eStyleUnit_None,
277 "GetIntrinsicSize should have reported no "
278 "intrinsic width");
279 float val = width.GetAnimValInSpecifiedUnits() / 100.0f;
280 if (val < 0.0f) val = 0.0f;
281 intrinsicSize.width.SetCoordValue(val * cbSize.width);
282 }
284 nsSVGLength2 &height =
285 content->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT];
286 NS_ASSERTION(aCBSize.height != NS_AUTOHEIGHT,
287 "root should not have auto-height containing block");
288 if (height.IsPercentage()) {
289 NS_ABORT_IF_FALSE(intrinsicSize.height.GetUnit() == eStyleUnit_None,
290 "GetIntrinsicSize should have reported no "
291 "intrinsic height");
292 float val = height.GetAnimValInSpecifiedUnits() / 100.0f;
293 if (val < 0.0f) val = 0.0f;
294 intrinsicSize.height.SetCoordValue(val * cbSize.height);
295 }
296 NS_ABORT_IF_FALSE(intrinsicSize.height.GetUnit() == eStyleUnit_Coord &&
297 intrinsicSize.width.GetUnit() == eStyleUnit_Coord,
298 "We should have just handled the only situation where"
299 "we lack an intrinsic height or width.");
300 }
302 return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(
303 aRenderingContext, this,
304 intrinsicSize, GetIntrinsicRatio(), cbSize,
305 aMargin, aBorder, aPadding);
306 }
308 nsresult
309 nsSVGOuterSVGFrame::Reflow(nsPresContext* aPresContext,
310 nsHTMLReflowMetrics& aDesiredSize,
311 const nsHTMLReflowState& aReflowState,
312 nsReflowStatus& aStatus)
313 {
314 DO_GLOBAL_REFLOW_COUNT("nsSVGOuterSVGFrame");
315 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
316 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
317 ("enter nsSVGOuterSVGFrame::Reflow: availSize=%d,%d",
318 aReflowState.AvailableWidth(), aReflowState.AvailableHeight()));
320 NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
322 aStatus = NS_FRAME_COMPLETE;
324 aDesiredSize.Width() = aReflowState.ComputedWidth() +
325 aReflowState.ComputedPhysicalBorderPadding().LeftRight();
326 aDesiredSize.Height() = aReflowState.ComputedHeight() +
327 aReflowState.ComputedPhysicalBorderPadding().TopBottom();
329 NS_ASSERTION(!GetPrevInFlow(), "SVG can't currently be broken across pages.");
331 SVGSVGElement *svgElem = static_cast<SVGSVGElement*>(mContent);
333 nsSVGOuterSVGAnonChildFrame *anonKid =
334 static_cast<nsSVGOuterSVGAnonChildFrame*>(GetFirstPrincipalChild());
336 if (mState & NS_FRAME_FIRST_REFLOW) {
337 // Initialize
338 svgElem->UpdateHasChildrenOnlyTransform();
339 }
341 // If our SVG viewport has changed, update our content and notify.
342 // http://www.w3.org/TR/SVG11/coords.html#ViewportSpace
344 svgFloatSize newViewportSize(
345 nsPresContext::AppUnitsToFloatCSSPixels(aReflowState.ComputedWidth()),
346 nsPresContext::AppUnitsToFloatCSSPixels(aReflowState.ComputedHeight()));
348 svgFloatSize oldViewportSize = svgElem->GetViewportSize();
350 uint32_t changeBits = 0;
351 if (newViewportSize != oldViewportSize) {
352 // When our viewport size changes, we may need to update the overflow rects
353 // of our child frames. This is the case if:
354 //
355 // * We have a real/synthetic viewBox (a children-only transform), since
356 // the viewBox transform will change as the viewport dimensions change.
357 //
358 // * We do not have a real/synthetic viewBox, but the last time we
359 // reflowed (or the last time UpdateOverflow() was called) we did.
360 //
361 // We only handle the former case here, in which case we mark all our child
362 // frames as dirty so that we reflow them below and update their overflow
363 // rects.
364 //
365 // In the latter case, updating of overflow rects is handled for removal of
366 // real viewBox (the viewBox attribute) in AttributeChanged. Synthetic
367 // viewBox "removal" (e.g. a document references the same SVG via both an
368 // <svg:image> and then as a CSS background image (a synthetic viewBox is
369 // used when painting the former, but not when painting the latter)) is
370 // handled in SVGSVGElement::FlushImageTransformInvalidation.
371 //
372 if (svgElem->HasViewBoxOrSyntheticViewBox()) {
373 nsIFrame* anonChild = GetFirstPrincipalChild();
374 anonChild->AddStateBits(NS_FRAME_IS_DIRTY);
375 for (nsIFrame* child = anonChild->GetFirstPrincipalChild(); child;
376 child = child->GetNextSibling()) {
377 child->AddStateBits(NS_FRAME_IS_DIRTY);
378 }
379 }
380 changeBits |= COORD_CONTEXT_CHANGED;
381 svgElem->SetViewportSize(newViewportSize);
382 }
383 if (mFullZoom != PresContext()->GetFullZoom()) {
384 changeBits |= FULL_ZOOM_CHANGED;
385 mFullZoom = PresContext()->GetFullZoom();
386 }
387 if (changeBits) {
388 NotifyViewportOrTransformChanged(changeBits);
389 }
390 mViewportInitialized = true;
392 // Now that we've marked the necessary children as dirty, call
393 // ReflowSVG() or ReflowSVGNonDisplayText() on them, depending
394 // on whether we are non-display.
395 mCallingReflowSVG = true;
396 if (GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
397 ReflowSVGNonDisplayText(this);
398 } else {
399 // Update the mRects and visual overflow rects of all our descendants,
400 // including our anonymous wrapper kid:
401 anonKid->AddStateBits(mState & NS_FRAME_IS_DIRTY);
402 anonKid->ReflowSVG();
403 NS_ABORT_IF_FALSE(!anonKid->GetNextSibling(),
404 "We should have one anonymous child frame wrapping our real children");
405 }
406 mCallingReflowSVG = false;
408 // Set our anonymous kid's offset from our border box:
409 anonKid->SetPosition(GetContentRectRelativeToSelf().TopLeft());
411 // Including our size in our overflow rects regardless of the value of
412 // 'background', 'border', etc. makes sure that we usually (when we clip to
413 // our content area) don't have to keep changing our overflow rects as our
414 // descendants move about (see perf comment below). Including our size in our
415 // scrollable overflow rect also makes sure that we scroll if we're too big
416 // for our viewport.
417 //
418 // <svg> never allows scrolling to anything outside its mRect (only panning),
419 // so we must always keep our scrollable overflow set to our size.
420 //
421 // With regards to visual overflow, we always clip root-<svg> (see our
422 // BuildDisplayList method) regardless of the value of the 'overflow'
423 // property since that is per-spec, even for the initial 'visible' value. For
424 // that reason there's no point in adding descendant visual overflow to our
425 // own when this frame is for a root-<svg>. That said, there's also a very
426 // good performance reason for us wanting to avoid doing so. If we did, then
427 // the frame's overflow would often change as descendants that are partially
428 // or fully outside its rect moved (think animation on/off screen), and that
429 // would cause us to do a full NS_FRAME_IS_DIRTY reflow and repaint of the
430 // entire document tree each such move (see bug 875175).
431 //
432 // So it's only non-root outer-<svg> that has the visual overflow of its
433 // descendants added to its own. (Note that the default user-agent style
434 // sheet makes 'hidden' the default value for :not(root(svg)), so usually
435 // FinishAndStoreOverflow will still clip this back to the frame's rect.)
436 //
437 // WARNING!! Keep UpdateBounds below in sync with whatever we do for our
438 // overflow rects here! (Again, see bug 875175.)
439 //
440 aDesiredSize.SetOverflowAreasToDesiredBounds();
441 if (!mIsRootContent) {
442 aDesiredSize.mOverflowAreas.VisualOverflow().UnionRect(
443 aDesiredSize.mOverflowAreas.VisualOverflow(),
444 anonKid->GetVisualOverflowRect() + anonKid->GetPosition());
445 }
446 FinishAndStoreOverflow(&aDesiredSize);
448 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
449 ("exit nsSVGOuterSVGFrame::Reflow: size=%d,%d",
450 aDesiredSize.Width(), aDesiredSize.Height()));
451 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
452 return NS_OK;
453 }
455 nsresult
456 nsSVGOuterSVGFrame::DidReflow(nsPresContext* aPresContext,
457 const nsHTMLReflowState* aReflowState,
458 nsDidReflowStatus aStatus)
459 {
460 nsresult rv = nsSVGOuterSVGFrameBase::DidReflow(aPresContext,aReflowState,aStatus);
462 // Make sure elements styled by :hover get updated if script/animation moves
463 // them under or out from under the pointer:
464 PresContext()->PresShell()->SynthesizeMouseMove(false);
466 return rv;
467 }
469 /* virtual */ bool
470 nsSVGOuterSVGFrame::UpdateOverflow()
471 {
472 // See the comments in Reflow above.
474 // WARNING!! Keep this in sync with Reflow above!
476 nsRect rect(nsPoint(0, 0), GetSize());
477 nsOverflowAreas overflowAreas(rect, rect);
479 if (!mIsRootContent) {
480 nsIFrame *anonKid = GetFirstPrincipalChild();
481 overflowAreas.VisualOverflow().UnionRect(
482 overflowAreas.VisualOverflow(),
483 anonKid->GetVisualOverflowRect() + anonKid->GetPosition());
484 }
486 return FinishAndStoreOverflow(overflowAreas, GetSize());
487 }
490 //----------------------------------------------------------------------
491 // container methods
493 /**
494 * Used to paint/hit-test SVG when SVG display lists are disabled.
495 */
496 class nsDisplayOuterSVG : public nsDisplayItem {
497 public:
498 nsDisplayOuterSVG(nsDisplayListBuilder* aBuilder,
499 nsSVGOuterSVGFrame* aFrame) :
500 nsDisplayItem(aBuilder, aFrame) {
501 MOZ_COUNT_CTOR(nsDisplayOuterSVG);
502 }
503 #ifdef NS_BUILD_REFCNT_LOGGING
504 virtual ~nsDisplayOuterSVG() {
505 MOZ_COUNT_DTOR(nsDisplayOuterSVG);
506 }
507 #endif
509 virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
510 HitTestState* aState,
511 nsTArray<nsIFrame*> *aOutFrames) MOZ_OVERRIDE;
512 virtual void Paint(nsDisplayListBuilder* aBuilder,
513 nsRenderingContext* aCtx) MOZ_OVERRIDE;
515 virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
516 const nsDisplayItemGeometry* aGeometry,
517 nsRegion* aInvalidRegion) MOZ_OVERRIDE;
519 NS_DISPLAY_DECL_NAME("SVGOuterSVG", TYPE_SVG_OUTER_SVG)
520 };
522 void
523 nsDisplayOuterSVG::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
524 HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
525 {
526 nsSVGOuterSVGFrame *outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(mFrame);
527 nsRect rectAtOrigin = aRect - ToReferenceFrame();
528 nsRect thisRect(nsPoint(0,0), outerSVGFrame->GetSize());
529 if (!thisRect.Intersects(rectAtOrigin))
530 return;
532 nsPoint rectCenter(rectAtOrigin.x + rectAtOrigin.width / 2,
533 rectAtOrigin.y + rectAtOrigin.height / 2);
535 nsSVGOuterSVGAnonChildFrame *anonKid =
536 static_cast<nsSVGOuterSVGAnonChildFrame*>(
537 outerSVGFrame->GetFirstPrincipalChild());
538 nsIFrame* frame = nsSVGUtils::HitTestChildren(
539 anonKid, rectCenter + outerSVGFrame->GetPosition() -
540 outerSVGFrame->GetContentRect().TopLeft());
541 if (frame) {
542 aOutFrames->AppendElement(frame);
543 }
544 }
546 void
547 nsDisplayOuterSVG::Paint(nsDisplayListBuilder* aBuilder,
548 nsRenderingContext* aContext)
549 {
550 #if defined(DEBUG) && defined(SVG_DEBUG_PAINT_TIMING)
551 PRTime start = PR_Now();
552 #endif
554 // Create an SVGAutoRenderState so we can call SetPaintingToWindow on
555 // it, but do so without changing the render mode:
556 SVGAutoRenderState state(aContext, SVGAutoRenderState::GetRenderMode(aContext));
558 if (aBuilder->IsPaintingToWindow()) {
559 state.SetPaintingToWindow(true);
560 }
562 nsRect viewportRect =
563 mFrame->GetContentRectRelativeToSelf() + ToReferenceFrame();
565 nsRect clipRect = mVisibleRect.Intersect(viewportRect);
567 nsIntRect contentAreaDirtyRect =
568 (clipRect - viewportRect.TopLeft()).
569 ToOutsidePixels(mFrame->PresContext()->AppUnitsPerDevPixel());
571 aContext->PushState();
572 aContext->Translate(viewportRect.TopLeft());
573 nsSVGUtils::PaintFrameWithEffects(aContext, &contentAreaDirtyRect, mFrame);
574 aContext->PopState();
576 NS_ASSERTION(!aContext->ThebesContext()->HasError(), "Cairo in error state");
578 #if defined(DEBUG) && defined(SVG_DEBUG_PAINT_TIMING)
579 PRTime end = PR_Now();
580 printf("SVG Paint Timing: %f ms\n", (end-start)/1000.0);
581 #endif
582 }
584 static PLDHashOperator CheckForeignObjectInvalidatedArea(nsPtrHashKey<nsSVGForeignObjectFrame>* aEntry, void* aData)
585 {
586 nsRegion* region = static_cast<nsRegion*>(aData);
587 region->Or(*region, aEntry->GetKey()->GetInvalidRegion());
588 return PL_DHASH_NEXT;
589 }
591 nsRegion
592 nsSVGOuterSVGFrame::FindInvalidatedForeignObjectFrameChildren(nsIFrame* aFrame)
593 {
594 nsRegion result;
595 if (mForeignObjectHash && mForeignObjectHash->Count()) {
596 mForeignObjectHash->EnumerateEntries(CheckForeignObjectInvalidatedArea, &result);
597 }
598 return result;
599 }
601 void
602 nsDisplayOuterSVG::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
603 const nsDisplayItemGeometry* aGeometry,
604 nsRegion* aInvalidRegion)
605 {
606 nsSVGOuterSVGFrame *frame = static_cast<nsSVGOuterSVGFrame*>(mFrame);
607 frame->InvalidateSVG(frame->FindInvalidatedForeignObjectFrameChildren(frame));
609 nsRegion result = frame->GetInvalidRegion();
610 result.MoveBy(ToReferenceFrame());
611 frame->ClearInvalidRegion();
613 nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
614 aInvalidRegion->Or(*aInvalidRegion, result);
615 }
617 // helper
618 static inline bool
619 DependsOnIntrinsicSize(const nsIFrame* aEmbeddingFrame)
620 {
621 const nsStylePosition *pos = aEmbeddingFrame->StylePosition();
622 const nsStyleCoord &width = pos->mWidth;
623 const nsStyleCoord &height = pos->mHeight;
625 // XXX it would be nice to know if the size of aEmbeddingFrame's containing
626 // block depends on aEmbeddingFrame, then we'd know if we can return false
627 // for eStyleUnit_Percent too.
628 return !width.ConvertsToLength() ||
629 !height.ConvertsToLength();
630 }
632 nsresult
633 nsSVGOuterSVGFrame::AttributeChanged(int32_t aNameSpaceID,
634 nsIAtom* aAttribute,
635 int32_t aModType)
636 {
637 if (aNameSpaceID == kNameSpaceID_None &&
638 !(GetStateBits() & (NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_NONDISPLAY))) {
639 if (aAttribute == nsGkAtoms::viewBox ||
640 aAttribute == nsGkAtoms::preserveAspectRatio ||
641 aAttribute == nsGkAtoms::transform) {
643 // make sure our cached transform matrix gets (lazily) updated
644 mCanvasTM = nullptr;
646 nsSVGUtils::NotifyChildrenOfSVGChange(GetFirstPrincipalChild(),
647 aAttribute == nsGkAtoms::viewBox ?
648 TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED : TRANSFORM_CHANGED);
650 if (aAttribute != nsGkAtoms::transform) {
651 static_cast<SVGSVGElement*>(mContent)->ChildrenOnlyTransformChanged();
652 }
654 } else if (aAttribute == nsGkAtoms::width ||
655 aAttribute == nsGkAtoms::height) {
657 // Don't call ChildrenOnlyTransformChanged() here, since we call it
658 // under Reflow if the width/height actually changed.
660 nsIFrame* embeddingFrame;
661 if (IsRootOfReplacedElementSubDoc(&embeddingFrame) && embeddingFrame) {
662 if (DependsOnIntrinsicSize(embeddingFrame)) {
663 // Tell embeddingFrame's presShell it needs to be reflowed (which takes
664 // care of reflowing us too).
665 embeddingFrame->PresContext()->PresShell()->
666 FrameNeedsReflow(embeddingFrame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
667 }
668 // else our width and height is overridden - don't reflow anything
669 } else {
670 // We are not embedded by reference, so our 'width' and 'height'
671 // attributes are not overridden - we need to reflow.
672 PresContext()->PresShell()->
673 FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
674 }
675 }
676 }
678 return NS_OK;
679 }
681 //----------------------------------------------------------------------
682 // painting
684 void
685 nsSVGOuterSVGFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
686 const nsRect& aDirtyRect,
687 const nsDisplayListSet& aLists)
688 {
689 if (GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
690 return;
691 }
693 DisplayBorderBackgroundOutline(aBuilder, aLists);
695 // Per-spec, we always clip root-<svg> even when 'overflow' has its initial
696 // value of 'visible'. See also the "visual overflow" comments in Reflow.
697 DisplayListClipState::AutoSaveRestore autoSR(aBuilder);
698 if (mIsRootContent ||
699 StyleDisplay()->IsScrollableOverflow()) {
700 autoSR.ClipContainingBlockDescendantsToContentBox(aBuilder, this);
701 }
703 if ((aBuilder->IsForEventDelivery() &&
704 NS_SVGDisplayListHitTestingEnabled()) ||
705 NS_SVGDisplayListPaintingEnabled()) {
706 nsDisplayList *contentList = aLists.Content();
707 nsDisplayListSet set(contentList, contentList, contentList,
708 contentList, contentList, contentList);
709 BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, set);
710 } else {
711 aLists.Content()->AppendNewToTop(
712 new (aBuilder) nsDisplayOuterSVG(aBuilder, this));
713 }
714 }
716 nsSplittableType
717 nsSVGOuterSVGFrame::GetSplittableType() const
718 {
719 return NS_FRAME_NOT_SPLITTABLE;
720 }
722 nsIAtom *
723 nsSVGOuterSVGFrame::GetType() const
724 {
725 return nsGkAtoms::svgOuterSVGFrame;
726 }
728 //----------------------------------------------------------------------
729 // nsISVGSVGFrame methods:
731 void
732 nsSVGOuterSVGFrame::NotifyViewportOrTransformChanged(uint32_t aFlags)
733 {
734 NS_ABORT_IF_FALSE(aFlags &&
735 !(aFlags & ~(COORD_CONTEXT_CHANGED | TRANSFORM_CHANGED |
736 FULL_ZOOM_CHANGED)),
737 "Unexpected aFlags value");
739 // No point in doing anything when were not init'ed yet:
740 if (!mViewportInitialized) {
741 return;
742 }
744 SVGSVGElement *content = static_cast<SVGSVGElement*>(mContent);
746 if (aFlags & COORD_CONTEXT_CHANGED) {
747 if (content->HasViewBoxRect()) {
748 // Percentage lengths on children resolve against the viewBox rect so we
749 // don't need to notify them of the viewport change, but the viewBox
750 // transform will have changed, so we need to notify them of that instead.
751 aFlags = TRANSFORM_CHANGED;
752 }
753 else if (content->ShouldSynthesizeViewBox()) {
754 // In the case of a synthesized viewBox, the synthetic viewBox's rect
755 // changes as the viewport changes. As a result we need to maintain the
756 // COORD_CONTEXT_CHANGED flag.
757 aFlags |= TRANSFORM_CHANGED;
758 }
759 else if (mCanvasTM && mCanvasTM->IsSingular()) {
760 // A width/height of zero will result in us having a singular mCanvasTM
761 // even when we don't have a viewBox. So we also want to recompute our
762 // mCanvasTM for this width/height change even though we don't have a
763 // viewBox.
764 aFlags |= TRANSFORM_CHANGED;
765 }
766 }
768 bool haveNonFulLZoomTransformChange = (aFlags & TRANSFORM_CHANGED);
770 if (aFlags & FULL_ZOOM_CHANGED) {
771 // Convert FULL_ZOOM_CHANGED to TRANSFORM_CHANGED:
772 aFlags = (aFlags & ~FULL_ZOOM_CHANGED) | TRANSFORM_CHANGED;
773 }
775 if (aFlags & TRANSFORM_CHANGED) {
776 // Make sure our canvas transform matrix gets (lazily) recalculated:
777 mCanvasTM = nullptr;
779 if (haveNonFulLZoomTransformChange &&
780 !(mState & NS_FRAME_IS_NONDISPLAY)) {
781 uint32_t flags = (mState & NS_FRAME_IN_REFLOW) ?
782 SVGSVGElement::eDuringReflow : 0;
783 content->ChildrenOnlyTransformChanged(flags);
784 }
785 }
787 nsSVGUtils::NotifyChildrenOfSVGChange(GetFirstPrincipalChild(), aFlags);
788 }
790 //----------------------------------------------------------------------
791 // nsISVGChildFrame methods:
793 nsresult
794 nsSVGOuterSVGFrame::PaintSVG(nsRenderingContext* aContext,
795 const nsIntRect *aDirtyRect,
796 nsIFrame* aTransformRoot)
797 {
798 NS_ASSERTION(GetFirstPrincipalChild()->GetType() ==
799 nsGkAtoms::svgOuterSVGAnonChildFrame &&
800 !GetFirstPrincipalChild()->GetNextSibling(),
801 "We should have a single, anonymous, child");
802 nsSVGOuterSVGAnonChildFrame *anonKid =
803 static_cast<nsSVGOuterSVGAnonChildFrame*>(GetFirstPrincipalChild());
804 return anonKid->PaintSVG(aContext, aDirtyRect, aTransformRoot);
805 }
807 SVGBBox
808 nsSVGOuterSVGFrame::GetBBoxContribution(const gfx::Matrix &aToBBoxUserspace,
809 uint32_t aFlags)
810 {
811 NS_ASSERTION(GetFirstPrincipalChild()->GetType() ==
812 nsGkAtoms::svgOuterSVGAnonChildFrame &&
813 !GetFirstPrincipalChild()->GetNextSibling(),
814 "We should have a single, anonymous, child");
815 // We must defer to our child so that we don't include our
816 // content->PrependLocalTransformsTo() transforms.
817 nsSVGOuterSVGAnonChildFrame *anonKid =
818 static_cast<nsSVGOuterSVGAnonChildFrame*>(GetFirstPrincipalChild());
819 return anonKid->GetBBoxContribution(aToBBoxUserspace, aFlags);
820 }
822 //----------------------------------------------------------------------
823 // nsSVGContainerFrame methods:
825 gfxMatrix
826 nsSVGOuterSVGFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot)
827 {
828 if (!(GetStateBits() & NS_FRAME_IS_NONDISPLAY) && !aTransformRoot) {
829 if ((aFor == FOR_PAINTING && NS_SVGDisplayListPaintingEnabled()) ||
830 (aFor == FOR_HIT_TESTING && NS_SVGDisplayListHitTestingEnabled())) {
831 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(this);
832 }
833 }
834 if (!mCanvasTM) {
835 NS_ASSERTION(!aTransformRoot, "transform root will be ignored here");
836 SVGSVGElement *content = static_cast<SVGSVGElement*>(mContent);
838 float devPxPerCSSPx =
839 1.0f / PresContext()->AppUnitsToFloatCSSPixels(
840 PresContext()->AppUnitsPerDevPixel());
842 gfxMatrix tm = content->PrependLocalTransformsTo(
843 gfxMatrix().Scale(devPxPerCSSPx, devPxPerCSSPx));
844 mCanvasTM = new gfxMatrix(tm);
845 }
846 return *mCanvasTM;
847 }
849 //----------------------------------------------------------------------
850 // Implementation helpers
852 bool
853 nsSVGOuterSVGFrame::IsRootOfReplacedElementSubDoc(nsIFrame **aEmbeddingFrame)
854 {
855 if (!mContent->GetParent()) {
856 // Our content is the document element
857 nsCOMPtr<nsISupports> container = PresContext()->GetContainerWeak();
858 nsCOMPtr<nsIDOMWindow> window = do_GetInterface(container);
859 if (window) {
860 nsCOMPtr<nsIDOMElement> frameElement;
861 window->GetFrameElement(getter_AddRefs(frameElement));
862 nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(frameElement);
863 if (olc) {
864 // Our document is inside an HTML 'object', 'embed' or 'applet' element
865 if (aEmbeddingFrame) {
866 nsCOMPtr<nsIContent> element = do_QueryInterface(frameElement);
867 *aEmbeddingFrame = element->GetPrimaryFrame();
868 NS_ASSERTION(*aEmbeddingFrame, "Yikes, no embedding frame!");
869 }
870 return true;
871 }
872 }
873 }
874 if (aEmbeddingFrame) {
875 *aEmbeddingFrame = nullptr;
876 }
877 return false;
878 }
880 bool
881 nsSVGOuterSVGFrame::IsRootOfImage()
882 {
883 if (!mContent->GetParent()) {
884 // Our content is the document element
885 nsIDocument* doc = mContent->GetCurrentDoc();
886 if (doc && doc->IsBeingUsedAsImage()) {
887 // Our document is being used as an image
888 return true;
889 }
890 }
892 return false;
893 }
895 bool
896 nsSVGOuterSVGFrame::VerticalScrollbarNotNeeded() const
897 {
898 nsSVGLength2 &height = static_cast<SVGSVGElement*>(mContent)->
899 mLengthAttributes[SVGSVGElement::ATTR_HEIGHT];
900 return height.IsPercentage() && height.GetBaseValInSpecifiedUnits() <= 100;
901 }
904 //----------------------------------------------------------------------
905 // Implementation of nsSVGOuterSVGAnonChildFrame
907 nsIFrame*
908 NS_NewSVGOuterSVGAnonChildFrame(nsIPresShell* aPresShell,
909 nsStyleContext* aContext)
910 {
911 return new (aPresShell) nsSVGOuterSVGAnonChildFrame(aContext);
912 }
914 NS_IMPL_FRAMEARENA_HELPERS(nsSVGOuterSVGAnonChildFrame)
916 #ifdef DEBUG
917 void
918 nsSVGOuterSVGAnonChildFrame::Init(nsIContent* aContent,
919 nsIFrame* aParent,
920 nsIFrame* aPrevInFlow)
921 {
922 NS_ABORT_IF_FALSE(aParent->GetType() == nsGkAtoms::svgOuterSVGFrame,
923 "Unexpected parent");
924 nsSVGOuterSVGAnonChildFrameBase::Init(aContent, aParent, aPrevInFlow);
925 }
926 #endif
928 nsIAtom *
929 nsSVGOuterSVGAnonChildFrame::GetType() const
930 {
931 return nsGkAtoms::svgOuterSVGAnonChildFrame;
932 }
934 bool
935 nsSVGOuterSVGAnonChildFrame::HasChildrenOnlyTransform(gfx::Matrix *aTransform) const
936 {
937 // We must claim our nsSVGOuterSVGFrame's children-only transforms as our own
938 // so that the children we are used to wrap are transformed properly.
940 SVGSVGElement *content = static_cast<SVGSVGElement*>(mContent);
942 bool hasTransform = content->HasChildrenOnlyTransform();
944 if (hasTransform && aTransform) {
945 // Outer-<svg> doesn't use x/y, so we can pass eChildToUserSpace here.
946 gfxMatrix identity;
947 *aTransform = gfx::ToMatrix(
948 content->PrependLocalTransformsTo(identity,
949 nsSVGElement::eChildToUserSpace));
950 }
952 return hasTransform;
953 }