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