layout/svg/nsSVGClipPathFrame.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 "nsSVGClipPathFrame.h"
michael@0 8
michael@0 9 // Keep others in (case-insensitive) order:
michael@0 10 #include "gfxContext.h"
michael@0 11 #include "nsGkAtoms.h"
michael@0 12 #include "nsRenderingContext.h"
michael@0 13 #include "mozilla/dom/SVGClipPathElement.h"
michael@0 14 #include "nsSVGEffects.h"
michael@0 15 #include "nsSVGUtils.h"
michael@0 16
michael@0 17 using namespace mozilla::dom;
michael@0 18
michael@0 19 //----------------------------------------------------------------------
michael@0 20 // Implementation
michael@0 21
michael@0 22 nsIFrame*
michael@0 23 NS_NewSVGClipPathFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
michael@0 24 {
michael@0 25 return new (aPresShell) nsSVGClipPathFrame(aContext);
michael@0 26 }
michael@0 27
michael@0 28 NS_IMPL_FRAMEARENA_HELPERS(nsSVGClipPathFrame)
michael@0 29
michael@0 30 nsresult
michael@0 31 nsSVGClipPathFrame::ClipPaint(nsRenderingContext* aContext,
michael@0 32 nsIFrame* aParent,
michael@0 33 const gfxMatrix &aMatrix)
michael@0 34 {
michael@0 35 // If the flag is set when we get here, it means this clipPath frame
michael@0 36 // has already been used painting the current clip, and the document
michael@0 37 // has a clip reference loop.
michael@0 38 if (mInUse) {
michael@0 39 NS_WARNING("Clip loop detected!");
michael@0 40 return NS_OK;
michael@0 41 }
michael@0 42 AutoClipPathReferencer clipRef(this);
michael@0 43
michael@0 44 mClipParent = aParent;
michael@0 45 if (mClipParentMatrix) {
michael@0 46 *mClipParentMatrix = aMatrix;
michael@0 47 } else {
michael@0 48 mClipParentMatrix = new gfxMatrix(aMatrix);
michael@0 49 }
michael@0 50
michael@0 51 gfxContext *gfx = aContext->ThebesContext();
michael@0 52
michael@0 53 nsISVGChildFrame *singleClipPathChild = nullptr;
michael@0 54
michael@0 55 if (IsTrivial(&singleClipPathChild)) {
michael@0 56 // Notify our child that it's painting as part of a clipPath, and that
michael@0 57 // we only require it to draw its path (it should skip filling, etc.):
michael@0 58 SVGAutoRenderState mode(aContext, SVGAutoRenderState::CLIP);
michael@0 59
michael@0 60 if (!singleClipPathChild) {
michael@0 61 // We have no children - the spec says clip away everything:
michael@0 62 gfx->Rectangle(gfxRect());
michael@0 63 } else {
michael@0 64 singleClipPathChild->NotifySVGChanged(
michael@0 65 nsISVGChildFrame::TRANSFORM_CHANGED);
michael@0 66 singleClipPathChild->PaintSVG(aContext, nullptr);
michael@0 67 }
michael@0 68 gfx->Clip();
michael@0 69 gfx->NewPath();
michael@0 70 return NS_OK;
michael@0 71 }
michael@0 72
michael@0 73 // Seems like this is a non-trivial clipPath, so we need to use a clip mask.
michael@0 74
michael@0 75 // Notify our children that they're painting into a clip mask:
michael@0 76 SVGAutoRenderState mode(aContext, SVGAutoRenderState::CLIP_MASK);
michael@0 77
michael@0 78 // Check if this clipPath is itself clipped by another clipPath:
michael@0 79 nsSVGClipPathFrame *clipPathFrame =
michael@0 80 nsSVGEffects::GetEffectProperties(this).GetClipPathFrame(nullptr);
michael@0 81 bool referencedClipIsTrivial;
michael@0 82 if (clipPathFrame) {
michael@0 83 referencedClipIsTrivial = clipPathFrame->IsTrivial();
michael@0 84 gfx->Save();
michael@0 85 if (referencedClipIsTrivial) {
michael@0 86 clipPathFrame->ClipPaint(aContext, aParent, aMatrix);
michael@0 87 } else {
michael@0 88 gfx->PushGroup(gfxContentType::ALPHA);
michael@0 89 }
michael@0 90 }
michael@0 91
michael@0 92 for (nsIFrame* kid = mFrames.FirstChild(); kid;
michael@0 93 kid = kid->GetNextSibling()) {
michael@0 94 nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
michael@0 95 if (SVGFrame) {
michael@0 96 // The CTM of each frame referencing us can be different.
michael@0 97 SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED);
michael@0 98
michael@0 99 bool isOK = true;
michael@0 100 nsSVGClipPathFrame *clipPathFrame =
michael@0 101 nsSVGEffects::GetEffectProperties(kid).GetClipPathFrame(&isOK);
michael@0 102 if (!isOK) {
michael@0 103 continue;
michael@0 104 }
michael@0 105
michael@0 106 bool isTrivial;
michael@0 107
michael@0 108 if (clipPathFrame) {
michael@0 109 isTrivial = clipPathFrame->IsTrivial();
michael@0 110 gfx->Save();
michael@0 111 if (isTrivial) {
michael@0 112 clipPathFrame->ClipPaint(aContext, aParent, aMatrix);
michael@0 113 } else {
michael@0 114 gfx->PushGroup(gfxContentType::ALPHA);
michael@0 115 }
michael@0 116 }
michael@0 117
michael@0 118 SVGFrame->PaintSVG(aContext, nullptr);
michael@0 119
michael@0 120 if (clipPathFrame) {
michael@0 121 if (!isTrivial) {
michael@0 122 gfx->PopGroupToSource();
michael@0 123
michael@0 124 nsRefPtr<gfxPattern> clipMaskSurface;
michael@0 125 gfx->PushGroup(gfxContentType::ALPHA);
michael@0 126
michael@0 127 clipPathFrame->ClipPaint(aContext, aParent, aMatrix);
michael@0 128 clipMaskSurface = gfx->PopGroup();
michael@0 129
michael@0 130 if (clipMaskSurface) {
michael@0 131 gfx->Mask(clipMaskSurface);
michael@0 132 }
michael@0 133 }
michael@0 134 gfx->Restore();
michael@0 135 }
michael@0 136 }
michael@0 137 }
michael@0 138
michael@0 139 if (clipPathFrame) {
michael@0 140 if (!referencedClipIsTrivial) {
michael@0 141 gfx->PopGroupToSource();
michael@0 142
michael@0 143 nsRefPtr<gfxPattern> clipMaskSurface;
michael@0 144 gfx->PushGroup(gfxContentType::ALPHA);
michael@0 145
michael@0 146 clipPathFrame->ClipPaint(aContext, aParent, aMatrix);
michael@0 147 clipMaskSurface = gfx->PopGroup();
michael@0 148
michael@0 149 if (clipMaskSurface) {
michael@0 150 gfx->Mask(clipMaskSurface);
michael@0 151 }
michael@0 152 }
michael@0 153 gfx->Restore();
michael@0 154 }
michael@0 155
michael@0 156 return NS_OK;
michael@0 157 }
michael@0 158
michael@0 159 bool
michael@0 160 nsSVGClipPathFrame::ClipHitTest(nsIFrame* aParent,
michael@0 161 const gfxMatrix &aMatrix,
michael@0 162 const nsPoint &aPoint)
michael@0 163 {
michael@0 164 // If the flag is set when we get here, it means this clipPath frame
michael@0 165 // has already been used in hit testing against the current clip,
michael@0 166 // and the document has a clip reference loop.
michael@0 167 if (mInUse) {
michael@0 168 NS_WARNING("Clip loop detected!");
michael@0 169 return false;
michael@0 170 }
michael@0 171 AutoClipPathReferencer clipRef(this);
michael@0 172
michael@0 173 mClipParent = aParent;
michael@0 174 if (mClipParentMatrix) {
michael@0 175 *mClipParentMatrix = aMatrix;
michael@0 176 } else {
michael@0 177 mClipParentMatrix = new gfxMatrix(aMatrix);
michael@0 178 }
michael@0 179
michael@0 180 nsSVGClipPathFrame *clipPathFrame =
michael@0 181 nsSVGEffects::GetEffectProperties(this).GetClipPathFrame(nullptr);
michael@0 182 if (clipPathFrame && !clipPathFrame->ClipHitTest(aParent, aMatrix, aPoint))
michael@0 183 return false;
michael@0 184
michael@0 185 for (nsIFrame* kid = mFrames.FirstChild(); kid;
michael@0 186 kid = kid->GetNextSibling()) {
michael@0 187 nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
michael@0 188 if (SVGFrame) {
michael@0 189 // Notify the child frame that we may be working with a
michael@0 190 // different transform, so it can update its covered region
michael@0 191 // (used to shortcut hit testing).
michael@0 192 SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED);
michael@0 193
michael@0 194 if (SVGFrame->GetFrameForPoint(aPoint))
michael@0 195 return true;
michael@0 196 }
michael@0 197 }
michael@0 198 return false;
michael@0 199 }
michael@0 200
michael@0 201 bool
michael@0 202 nsSVGClipPathFrame::IsTrivial(nsISVGChildFrame **aSingleChild)
michael@0 203 {
michael@0 204 // If the clip path is clipped then it's non-trivial
michael@0 205 if (nsSVGEffects::GetEffectProperties(this).GetClipPathFrame(nullptr))
michael@0 206 return false;
michael@0 207
michael@0 208 if (aSingleChild) {
michael@0 209 *aSingleChild = nullptr;
michael@0 210 }
michael@0 211
michael@0 212 nsISVGChildFrame *foundChild = nullptr;
michael@0 213
michael@0 214 for (nsIFrame* kid = mFrames.FirstChild(); kid;
michael@0 215 kid = kid->GetNextSibling()) {
michael@0 216 nsISVGChildFrame *svgChild = do_QueryFrame(kid);
michael@0 217 if (svgChild) {
michael@0 218 // We consider a non-trivial clipPath to be one containing
michael@0 219 // either more than one svg child and/or a svg container
michael@0 220 if (foundChild || svgChild->IsDisplayContainer())
michael@0 221 return false;
michael@0 222
michael@0 223 // or where the child is itself clipped
michael@0 224 if (nsSVGEffects::GetEffectProperties(kid).GetClipPathFrame(nullptr))
michael@0 225 return false;
michael@0 226
michael@0 227 foundChild = svgChild;
michael@0 228 }
michael@0 229 }
michael@0 230 if (aSingleChild) {
michael@0 231 *aSingleChild = foundChild;
michael@0 232 }
michael@0 233 return true;
michael@0 234 }
michael@0 235
michael@0 236 bool
michael@0 237 nsSVGClipPathFrame::IsValid()
michael@0 238 {
michael@0 239 if (mInUse) {
michael@0 240 NS_WARNING("Clip loop detected!");
michael@0 241 return false;
michael@0 242 }
michael@0 243 AutoClipPathReferencer clipRef(this);
michael@0 244
michael@0 245 bool isOK = true;
michael@0 246 nsSVGEffects::GetEffectProperties(this).GetClipPathFrame(&isOK);
michael@0 247 if (!isOK) {
michael@0 248 return false;
michael@0 249 }
michael@0 250
michael@0 251 for (nsIFrame* kid = mFrames.FirstChild(); kid;
michael@0 252 kid = kid->GetNextSibling()) {
michael@0 253
michael@0 254 nsIAtom *type = kid->GetType();
michael@0 255
michael@0 256 if (type == nsGkAtoms::svgUseFrame) {
michael@0 257 for (nsIFrame* grandKid = kid->GetFirstPrincipalChild(); grandKid;
michael@0 258 grandKid = grandKid->GetNextSibling()) {
michael@0 259
michael@0 260 nsIAtom *type = grandKid->GetType();
michael@0 261
michael@0 262 if (type != nsGkAtoms::svgPathGeometryFrame &&
michael@0 263 type != nsGkAtoms::svgTextFrame) {
michael@0 264 return false;
michael@0 265 }
michael@0 266 }
michael@0 267 continue;
michael@0 268 }
michael@0 269 if (type != nsGkAtoms::svgPathGeometryFrame &&
michael@0 270 type != nsGkAtoms::svgTextFrame) {
michael@0 271 return false;
michael@0 272 }
michael@0 273 }
michael@0 274 return true;
michael@0 275 }
michael@0 276
michael@0 277 nsresult
michael@0 278 nsSVGClipPathFrame::AttributeChanged(int32_t aNameSpaceID,
michael@0 279 nsIAtom* aAttribute,
michael@0 280 int32_t aModType)
michael@0 281 {
michael@0 282 if (aNameSpaceID == kNameSpaceID_None) {
michael@0 283 if (aAttribute == nsGkAtoms::transform) {
michael@0 284 nsSVGEffects::InvalidateDirectRenderingObservers(this);
michael@0 285 nsSVGUtils::NotifyChildrenOfSVGChange(this,
michael@0 286 nsISVGChildFrame::TRANSFORM_CHANGED);
michael@0 287 }
michael@0 288 if (aAttribute == nsGkAtoms::clipPathUnits) {
michael@0 289 nsSVGEffects::InvalidateRenderingObservers(this);
michael@0 290 }
michael@0 291 }
michael@0 292
michael@0 293 return nsSVGClipPathFrameBase::AttributeChanged(aNameSpaceID,
michael@0 294 aAttribute, aModType);
michael@0 295 }
michael@0 296
michael@0 297 void
michael@0 298 nsSVGClipPathFrame::Init(nsIContent* aContent,
michael@0 299 nsIFrame* aParent,
michael@0 300 nsIFrame* aPrevInFlow)
michael@0 301 {
michael@0 302 NS_ASSERTION(aContent->IsSVG(nsGkAtoms::clipPath),
michael@0 303 "Content is not an SVG clipPath!");
michael@0 304
michael@0 305 AddStateBits(NS_STATE_SVG_CLIPPATH_CHILD);
michael@0 306 nsSVGClipPathFrameBase::Init(aContent, aParent, aPrevInFlow);
michael@0 307 }
michael@0 308
michael@0 309 nsIAtom *
michael@0 310 nsSVGClipPathFrame::GetType() const
michael@0 311 {
michael@0 312 return nsGkAtoms::svgClipPathFrame;
michael@0 313 }
michael@0 314
michael@0 315 gfxMatrix
michael@0 316 nsSVGClipPathFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot)
michael@0 317 {
michael@0 318 SVGClipPathElement *content = static_cast<SVGClipPathElement*>(mContent);
michael@0 319
michael@0 320 gfxMatrix tm =
michael@0 321 content->PrependLocalTransformsTo(mClipParentMatrix ?
michael@0 322 *mClipParentMatrix : gfxMatrix());
michael@0 323
michael@0 324 return nsSVGUtils::AdjustMatrixForUnits(tm,
michael@0 325 &content->mEnumAttributes[SVGClipPathElement::CLIPPATHUNITS],
michael@0 326 mClipParent);
michael@0 327 }

mercurial