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.

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

mercurial