layout/svg/nsSVGOuterSVGFrame.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

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 }

mercurial