layout/svg/nsSVGFilterInstance.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 "nsSVGFilterInstance.h"
     9 // Keep others in (case-insensitive) order:
    10 #include "gfxPlatform.h"
    11 #include "gfxUtils.h"
    12 #include "nsISVGChildFrame.h"
    13 #include "nsRenderingContext.h"
    14 #include "mozilla/dom/SVGFilterElement.h"
    15 #include "nsReferencedElement.h"
    16 #include "nsSVGFilterFrame.h"
    17 #include "nsSVGFilterPaintCallback.h"
    18 #include "nsSVGUtils.h"
    19 #include "SVGContentUtils.h"
    20 #include "FilterSupport.h"
    21 #include "gfx2DGlue.h"
    23 using namespace mozilla;
    24 using namespace mozilla::dom;
    25 using namespace mozilla::gfx;
    27 nsSVGFilterInstance::nsSVGFilterInstance(const nsStyleFilter& aFilter,
    28                                          nsIFrame *aTargetFrame,
    29                                          const gfxRect& aTargetBBox,
    30                                          const gfxSize& aUserSpaceToFilterSpaceScale,
    31                                          const gfxSize& aFilterSpaceToUserSpaceScale) :
    32   mFilter(aFilter),
    33   mTargetFrame(aTargetFrame),
    34   mTargetBBox(aTargetBBox),
    35   mUserSpaceToFilterSpaceScale(aUserSpaceToFilterSpaceScale),
    36   mFilterSpaceToUserSpaceScale(aFilterSpaceToUserSpaceScale),
    37   mInitialized(false) {
    39   // Get the filter frame.
    40   mFilterFrame = GetFilterFrame();
    41   if (!mFilterFrame) {
    42     return;
    43   }
    45   // Get the filter element.
    46   mFilterElement = mFilterFrame->GetFilterContent();
    47   if (!mFilterElement) {
    48     NS_NOTREACHED("filter frame should have a related element");
    49     return;
    50   }
    52   mPrimitiveUnits =
    53     mFilterFrame->GetEnumValue(SVGFilterElement::PRIMITIVEUNITS);
    55   nsresult rv = ComputeBounds();
    56   if (NS_FAILED(rv)) {
    57     return;
    58   }
    60   mInitialized = true;
    61 }
    63 nsresult
    64 nsSVGFilterInstance::ComputeBounds()
    65 {
    66   // XXX if filterUnits is set (or has defaulted) to objectBoundingBox, we
    67   // should send a warning to the error console if the author has used lengths
    68   // with units. This is a common mistake and can result in the filter region
    69   // being *massive* below (because we ignore the units and interpret the number
    70   // as a factor of the bbox width/height). We should also send a warning if the
    71   // user uses a number without units (a future SVG spec should really
    72   // deprecate that, since it's too confusing for a bare number to be sometimes
    73   // interpreted as a fraction of the bounding box and sometimes as user-space
    74   // units). So really only percentage values should be used in this case.
    76   // Set the user space bounds (i.e. the filter region in user space).
    77   nsSVGLength2 XYWH[4];
    78   NS_ABORT_IF_FALSE(sizeof(mFilterElement->mLengthAttributes) == sizeof(XYWH),
    79                     "XYWH size incorrect");
    80   memcpy(XYWH, mFilterElement->mLengthAttributes, 
    81     sizeof(mFilterElement->mLengthAttributes));
    82   XYWH[0] = *mFilterFrame->GetLengthValue(SVGFilterElement::ATTR_X);
    83   XYWH[1] = *mFilterFrame->GetLengthValue(SVGFilterElement::ATTR_Y);
    84   XYWH[2] = *mFilterFrame->GetLengthValue(SVGFilterElement::ATTR_WIDTH);
    85   XYWH[3] = *mFilterFrame->GetLengthValue(SVGFilterElement::ATTR_HEIGHT);
    86   uint16_t filterUnits =
    87     mFilterFrame->GetEnumValue(SVGFilterElement::FILTERUNITS);
    88   mUserSpaceBounds = nsSVGUtils::GetRelativeRect(filterUnits,
    89     XYWH, mTargetBBox, mTargetFrame);
    91   // Temporarily transform the user space bounds to filter space, so we
    92   // can align them with the pixel boundries of the offscreen surface.
    93   // The offscreen surface has the same scale as filter space.
    94   mUserSpaceBounds = UserSpaceToFilterSpace(mUserSpaceBounds);
    95   mUserSpaceBounds.RoundOut();
    96   if (mUserSpaceBounds.Width() <= 0 || mUserSpaceBounds.Height() <= 0) {
    97     // 0 disables rendering, < 0 is error. dispatch error console warning
    98     // or error as appropriate.
    99     return NS_ERROR_FAILURE;
   100   }
   102   // Set the filter space bounds.
   103   if (!gfxUtils::GfxRectToIntRect(mUserSpaceBounds, &mFilterSpaceBounds)) {
   104     // The filter region is way too big if there is float -> int overflow.
   105     return NS_ERROR_FAILURE;
   106   }
   108   // Undo the temporary transformation of the user space bounds.
   109   mUserSpaceBounds = FilterSpaceToUserSpace(mUserSpaceBounds);
   111   return NS_OK;
   112 }
   114 nsSVGFilterFrame*
   115 nsSVGFilterInstance::GetFilterFrame()
   116 {
   117   if (mFilter.GetType() != NS_STYLE_FILTER_URL) {
   118     // The filter is not an SVG reference filter.
   119     return nullptr;
   120   }
   122   nsIURI* url = mFilter.GetURL();
   123   if (!url) {
   124     NS_NOTREACHED("an nsStyleFilter of type URL should have a non-null URL");
   125     return nullptr;
   126   }
   128   // Get the target element to use as a point of reference for looking up the
   129   // filter element.
   130   nsIContent* targetElement = mTargetFrame->GetContent();
   131   if (!targetElement) {
   132     // There is no element associated with the target frame.
   133     return nullptr;
   134   }
   136   // Look up the filter element by URL.
   137   nsReferencedElement filterElement;
   138   bool watch = false;
   139   filterElement.Reset(targetElement, url, watch);
   140   Element* element = filterElement.get();
   141   if (!element) {
   142     // The URL points to no element.
   143     return nullptr;
   144   }
   146   // Get the frame of the filter element.
   147   nsIFrame* frame = element->GetPrimaryFrame();
   148   if (frame->GetType() != nsGkAtoms::svgFilterFrame) {
   149     // The URL points to an element that's not an SVG filter element.
   150     return nullptr;
   151   }
   153   return static_cast<nsSVGFilterFrame*>(frame);
   154 }
   156 float
   157 nsSVGFilterInstance::GetPrimitiveNumber(uint8_t aCtxType, float aValue) const
   158 {
   159   nsSVGLength2 val;
   160   val.Init(aCtxType, 0xff, aValue,
   161            nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
   163   float value;
   164   if (mPrimitiveUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
   165     value = nsSVGUtils::ObjectSpace(mTargetBBox, &val);
   166   } else {
   167     value = nsSVGUtils::UserSpace(mTargetFrame, &val);
   168   }
   170   switch (aCtxType) {
   171   case SVGContentUtils::X:
   172     return value * mUserSpaceToFilterSpaceScale.width;
   173   case SVGContentUtils::Y:
   174     return value * mUserSpaceToFilterSpaceScale.height;
   175   case SVGContentUtils::XY:
   176   default:
   177     return value * SVGContentUtils::ComputeNormalizedHypotenuse(
   178                      mUserSpaceToFilterSpaceScale.width,
   179                      mUserSpaceToFilterSpaceScale.height);
   180   }
   181 }
   183 Point3D
   184 nsSVGFilterInstance::ConvertLocation(const Point3D& aPoint) const
   185 {
   186   nsSVGLength2 val[4];
   187   val[0].Init(SVGContentUtils::X, 0xff, aPoint.x,
   188               nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
   189   val[1].Init(SVGContentUtils::Y, 0xff, aPoint.y,
   190               nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
   191   // Dummy width/height values
   192   val[2].Init(SVGContentUtils::X, 0xff, 0,
   193               nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
   194   val[3].Init(SVGContentUtils::Y, 0xff, 0,
   195               nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
   197   gfxRect feArea = nsSVGUtils::GetRelativeRect(mPrimitiveUnits,
   198     val, mTargetBBox, mTargetFrame);
   199   gfxRect r = UserSpaceToFilterSpace(feArea);
   200   return Point3D(r.x, r.y, GetPrimitiveNumber(SVGContentUtils::XY, aPoint.z));
   201 }
   203 gfxRect
   204 nsSVGFilterInstance::UserSpaceToFilterSpace(const gfxRect& aUserSpaceRect) const
   205 {
   206   gfxRect filterSpaceRect = aUserSpaceRect;
   207   filterSpaceRect.Scale(mUserSpaceToFilterSpaceScale.width,
   208                         mUserSpaceToFilterSpaceScale.height);
   209   return filterSpaceRect;
   210 }
   212 gfxRect
   213 nsSVGFilterInstance::FilterSpaceToUserSpace(const gfxRect& aFilterSpaceRect) const
   214 {
   215   gfxRect userSpaceRect = aFilterSpaceRect;
   216   userSpaceRect.Scale(mFilterSpaceToUserSpaceScale.width,
   217                       mFilterSpaceToUserSpaceScale.height);
   218   return userSpaceRect;
   219 }
   221 IntRect
   222 nsSVGFilterInstance::ComputeFilterPrimitiveSubregion(nsSVGFE* aFilterElement,
   223                                                      const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
   224                                                      const nsTArray<int32_t>& aInputIndices)
   225 {
   226   nsSVGFE* fE = aFilterElement;
   228   IntRect defaultFilterSubregion(0,0,0,0);
   229   if (fE->SubregionIsUnionOfRegions()) {
   230     for (uint32_t i = 0; i < aInputIndices.Length(); ++i) {
   231       int32_t inputIndex = aInputIndices[i];
   232       IntRect inputSubregion = inputIndex >= 0 ?
   233         aPrimitiveDescrs[inputIndex].PrimitiveSubregion() :
   234         ToIntRect(mFilterSpaceBounds);
   236       defaultFilterSubregion = defaultFilterSubregion.Union(inputSubregion);
   237     }
   238   } else {
   239     defaultFilterSubregion = ToIntRect(mFilterSpaceBounds);
   240   }
   242   gfxRect feArea = nsSVGUtils::GetRelativeRect(mPrimitiveUnits,
   243     &fE->mLengthAttributes[nsSVGFE::ATTR_X], mTargetBBox, mTargetFrame);
   244   Rect region = ToRect(UserSpaceToFilterSpace(feArea));
   246   if (!fE->mLengthAttributes[nsSVGFE::ATTR_X].IsExplicitlySet())
   247     region.x = defaultFilterSubregion.X();
   248   if (!fE->mLengthAttributes[nsSVGFE::ATTR_Y].IsExplicitlySet())
   249     region.y = defaultFilterSubregion.Y();
   250   if (!fE->mLengthAttributes[nsSVGFE::ATTR_WIDTH].IsExplicitlySet())
   251     region.width = defaultFilterSubregion.Width();
   252   if (!fE->mLengthAttributes[nsSVGFE::ATTR_HEIGHT].IsExplicitlySet())
   253     region.height = defaultFilterSubregion.Height();
   255   // We currently require filter primitive subregions to be pixel-aligned.
   256   // Following the spec, any pixel partially in the region is included
   257   // in the region.
   258   region.RoundOut();
   260   return RoundedToInt(region);
   261 }
   263 void
   264 nsSVGFilterInstance::GetInputsAreTainted(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
   265                                          const nsTArray<int32_t>& aInputIndices,
   266                                          nsTArray<bool>& aOutInputsAreTainted)
   267 {
   268   for (uint32_t i = 0; i < aInputIndices.Length(); i++) {
   269     int32_t inputIndex = aInputIndices[i];
   270     if (inputIndex < 0) {
   271       // SourceGraphic, SourceAlpha, FillPaint and StrokePaint are tainted.
   272       aOutInputsAreTainted.AppendElement(true);
   273     } else {
   274       aOutInputsAreTainted.AppendElement(aPrimitiveDescrs[inputIndex].IsTainted());
   275     }
   276   }
   277 }
   279 static int32_t
   280 GetLastResultIndex(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs)
   281 {
   282   uint32_t numPrimitiveDescrs = aPrimitiveDescrs.Length();
   283   return !numPrimitiveDescrs ?
   284     FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic :
   285     numPrimitiveDescrs - 1;
   286 }
   288 nsresult
   289 nsSVGFilterInstance::GetSourceIndices(nsSVGFE* aPrimitiveElement,
   290                                       const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
   291                                       const nsDataHashtable<nsStringHashKey, int32_t>& aImageTable,
   292                                       nsTArray<int32_t>& aSourceIndices)
   293 {
   294   nsAutoTArray<nsSVGStringInfo,2> sources;
   295   aPrimitiveElement->GetSourceImageNames(sources);
   297   for (uint32_t j = 0; j < sources.Length(); j++) {
   298     nsAutoString str;
   299     sources[j].mString->GetAnimValue(str, sources[j].mElement);
   301     int32_t sourceIndex = 0;
   302     if (str.EqualsLiteral("SourceGraphic")) {
   303       sourceIndex = mSourceGraphicIndex;
   304     } else if (str.EqualsLiteral("SourceAlpha")) {
   305       sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha;
   306     } else if (str.EqualsLiteral("FillPaint")) {
   307       sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexFillPaint;
   308     } else if (str.EqualsLiteral("StrokePaint")) {
   309       sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexStrokePaint;
   310     } else if (str.EqualsLiteral("BackgroundImage") ||
   311                str.EqualsLiteral("BackgroundAlpha")) {
   312       return NS_ERROR_NOT_IMPLEMENTED;
   313     } else if (str.EqualsLiteral("")) {
   314       sourceIndex = GetLastResultIndex(aPrimitiveDescrs);
   315     } else {
   316       bool inputExists = aImageTable.Get(str, &sourceIndex);
   317       if (!inputExists)
   318         return NS_ERROR_FAILURE;
   319     }
   321     aSourceIndices.AppendElement(sourceIndex);
   322   }
   323   return NS_OK;
   324 }
   326 nsresult
   327 nsSVGFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
   328                                      nsTArray<mozilla::RefPtr<SourceSurface>>& aInputImages)
   329 {
   330   mSourceGraphicIndex = GetLastResultIndex(aPrimitiveDescrs);
   332   // Get the filter primitive elements.
   333   nsTArray<nsRefPtr<nsSVGFE> > primitives;
   334   for (nsIContent* child = mFilterElement->nsINode::GetFirstChild();
   335        child;
   336        child = child->GetNextSibling()) {
   337     nsRefPtr<nsSVGFE> primitive;
   338     CallQueryInterface(child, (nsSVGFE**)getter_AddRefs(primitive));
   339     if (primitive) {
   340       primitives.AppendElement(primitive);
   341     }
   342   }
   344   // Maps source image name to source index.
   345   nsDataHashtable<nsStringHashKey, int32_t> imageTable(10);
   347   // The principal that we check principals of any loaded images against.
   348   nsCOMPtr<nsIPrincipal> principal = mTargetFrame->GetContent()->NodePrincipal();
   350   for (uint32_t primitiveElementIndex = 0;
   351        primitiveElementIndex < primitives.Length();
   352        ++primitiveElementIndex) {
   353     nsSVGFE* filter = primitives[primitiveElementIndex];
   355     nsAutoTArray<int32_t,2> sourceIndices;
   356     nsresult rv = GetSourceIndices(filter, aPrimitiveDescrs, imageTable, sourceIndices);
   357     if (NS_FAILED(rv)) {
   358       return rv;
   359     }
   361     IntRect primitiveSubregion =
   362       ComputeFilterPrimitiveSubregion(filter, aPrimitiveDescrs, sourceIndices);
   364     nsTArray<bool> sourcesAreTainted;
   365     GetInputsAreTainted(aPrimitiveDescrs, sourceIndices, sourcesAreTainted);
   367     FilterPrimitiveDescription descr =
   368       filter->GetPrimitiveDescription(this, primitiveSubregion, sourcesAreTainted, aInputImages);
   370     descr.SetIsTainted(filter->OutputIsTainted(sourcesAreTainted, principal));
   371     descr.SetPrimitiveSubregion(primitiveSubregion);
   373     for (uint32_t i = 0; i < sourceIndices.Length(); i++) {
   374       int32_t inputIndex = sourceIndices[i];
   375       descr.SetInputPrimitive(i, inputIndex);
   377       ColorSpace inputColorSpace = inputIndex >= 0
   378         ? aPrimitiveDescrs[inputIndex].OutputColorSpace()
   379         : ColorSpace(ColorSpace::SRGB);
   381       ColorSpace desiredInputColorSpace = filter->GetInputColorSpace(i, inputColorSpace);
   382       descr.SetInputColorSpace(i, desiredInputColorSpace);
   383       if (i == 0) {
   384         // the output color space is whatever in1 is if there is an in1
   385         descr.SetOutputColorSpace(desiredInputColorSpace);
   386       }
   387     }
   389     if (sourceIndices.Length() == 0) {
   390       descr.SetOutputColorSpace(filter->GetOutputColorSpace());
   391     }
   393     aPrimitiveDescrs.AppendElement(descr);
   394     uint32_t primitiveDescrIndex = aPrimitiveDescrs.Length() - 1;
   396     nsAutoString str;
   397     filter->GetResultImageName().GetAnimValue(str, filter);
   398     imageTable.Put(str, primitiveDescrIndex);
   399   }
   401   return NS_OK;
   402 }

mercurial