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 "nsSVGContainerFrame.h"
9 // Keep others in (case-insensitive) order:
10 #include "nsCSSFrameConstructor.h"
11 #include "nsSVGEffects.h"
12 #include "nsSVGElement.h"
13 #include "nsSVGUtils.h"
14 #include "nsSVGAnimatedTransformList.h"
15 #include "SVGTextFrame.h"
16 #include "RestyleManager.h"
18 using namespace mozilla;
20 NS_QUERYFRAME_HEAD(nsSVGContainerFrame)
21 NS_QUERYFRAME_ENTRY(nsSVGContainerFrame)
22 NS_QUERYFRAME_TAIL_INHERITING(nsSVGContainerFrameBase)
24 NS_QUERYFRAME_HEAD(nsSVGDisplayContainerFrame)
25 NS_QUERYFRAME_ENTRY(nsSVGDisplayContainerFrame)
26 NS_QUERYFRAME_ENTRY(nsISVGChildFrame)
27 NS_QUERYFRAME_TAIL_INHERITING(nsSVGContainerFrame)
29 nsIFrame*
30 NS_NewSVGContainerFrame(nsIPresShell* aPresShell,
31 nsStyleContext* aContext)
32 {
33 nsIFrame *frame = new (aPresShell) nsSVGContainerFrame(aContext);
34 // If we were called directly, then the frame is for a <defs> or
35 // an unknown element type. In both cases we prevent the content
36 // from displaying directly.
37 frame->AddStateBits(NS_FRAME_IS_NONDISPLAY);
38 return frame;
39 }
41 NS_IMPL_FRAMEARENA_HELPERS(nsSVGContainerFrame)
42 NS_IMPL_FRAMEARENA_HELPERS(nsSVGDisplayContainerFrame)
44 nsresult
45 nsSVGContainerFrame::AppendFrames(ChildListID aListID,
46 nsFrameList& aFrameList)
47 {
48 return InsertFrames(aListID, mFrames.LastChild(), aFrameList);
49 }
51 nsresult
52 nsSVGContainerFrame::InsertFrames(ChildListID aListID,
53 nsIFrame* aPrevFrame,
54 nsFrameList& aFrameList)
55 {
56 NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
57 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
58 "inserting after sibling frame with different parent");
60 mFrames.InsertFrames(this, aPrevFrame, aFrameList);
62 return NS_OK;
63 }
65 nsresult
66 nsSVGContainerFrame::RemoveFrame(ChildListID aListID,
67 nsIFrame* aOldFrame)
68 {
69 NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
71 mFrames.DestroyFrame(aOldFrame);
72 return NS_OK;
73 }
75 bool
76 nsSVGContainerFrame::UpdateOverflow()
77 {
78 if (mState & NS_FRAME_IS_NONDISPLAY) {
79 // We don't maintain overflow rects.
80 // XXX It would have be better if the restyle request hadn't even happened.
81 return false;
82 }
83 return nsSVGContainerFrameBase::UpdateOverflow();
84 }
86 /**
87 * Traverses a frame tree, marking any SVGTextFrame frames as dirty
88 * and calling InvalidateRenderingObservers() on it.
89 *
90 * The reason that this helper exists is because SVGTextFrame is special.
91 * None of the other SVG frames ever need to be reflowed when they have the
92 * NS_FRAME_IS_NONDISPLAY bit set on them because their PaintSVG methods
93 * (and those of any containers that they can validly be contained within) do
94 * not make use of mRect or overflow rects. "em" lengths, etc., are resolved
95 * as those elements are painted.
96 *
97 * SVGTextFrame is different because its anonymous block and inline frames
98 * need to be reflowed in order to get the correct metrics when things like
99 * inherited font-size of an ancestor changes, or a delayed webfont loads and
100 * applies.
101 *
102 * We assume that any change that requires the anonymous kid of an
103 * SVGTextFrame to reflow will result in an NS_FRAME_IS_DIRTY reflow. When
104 * that reflow reaches an NS_FRAME_IS_NONDISPLAY frame it would normally
105 * stop, but this helper looks for any SVGTextFrame descendants of such
106 * frames and marks them NS_FRAME_IS_DIRTY so that the next time that they are
107 * painted their anonymous kid will first get the necessary reflow.
108 */
109 /* static */ void
110 nsSVGContainerFrame::ReflowSVGNonDisplayText(nsIFrame* aContainer)
111 {
112 NS_ASSERTION(aContainer->GetStateBits() & NS_FRAME_IS_DIRTY,
113 "expected aContainer to be NS_FRAME_IS_DIRTY");
114 NS_ASSERTION((aContainer->GetStateBits() & NS_FRAME_IS_NONDISPLAY) ||
115 !aContainer->IsFrameOfType(nsIFrame::eSVG),
116 "it is wasteful to call ReflowSVGNonDisplayText on a container "
117 "frame that is not NS_FRAME_IS_NONDISPLAY");
118 for (nsIFrame* kid = aContainer->GetFirstPrincipalChild(); kid;
119 kid = kid->GetNextSibling()) {
120 nsIAtom* type = kid->GetType();
121 if (type == nsGkAtoms::svgTextFrame) {
122 static_cast<SVGTextFrame*>(kid)->ReflowSVGNonDisplayText();
123 } else {
124 if (kid->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer) ||
125 type == nsGkAtoms::svgForeignObjectFrame ||
126 !kid->IsFrameOfType(nsIFrame::eSVG)) {
127 ReflowSVGNonDisplayText(kid);
128 }
129 }
130 }
131 }
133 void
134 nsSVGDisplayContainerFrame::Init(nsIContent* aContent,
135 nsIFrame* aParent,
136 nsIFrame* aPrevInFlow)
137 {
138 if (!(GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
139 AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD);
140 }
141 nsSVGContainerFrame::Init(aContent, aParent, aPrevInFlow);
142 }
144 void
145 nsSVGDisplayContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
146 const nsRect& aDirtyRect,
147 const nsDisplayListSet& aLists)
148 {
149 // mContent could be a XUL element so check for an SVG element before casting
150 if (mContent->IsSVG() &&
151 !static_cast<const nsSVGElement*>(mContent)->HasValidDimensions()) {
152 return;
153 }
154 return BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists);
155 }
157 nsresult
158 nsSVGDisplayContainerFrame::InsertFrames(ChildListID aListID,
159 nsIFrame* aPrevFrame,
160 nsFrameList& aFrameList)
161 {
162 // memorize first old frame after insertion point
163 // XXXbz once again, this would work a lot better if the nsIFrame
164 // methods returned framelist iterators....
165 nsIFrame* nextFrame = aPrevFrame ?
166 aPrevFrame->GetNextSibling() : GetChildList(aListID).FirstChild();
167 nsIFrame* firstNewFrame = aFrameList.FirstChild();
169 // Insert the new frames
170 nsSVGContainerFrame::InsertFrames(aListID, aPrevFrame, aFrameList);
172 // If we are not a non-display SVG frame and we do not have a bounds update
173 // pending, then we need to schedule one for our new children:
174 if (!(GetStateBits() &
175 (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN |
176 NS_FRAME_IS_NONDISPLAY))) {
177 for (nsIFrame* kid = firstNewFrame; kid != nextFrame;
178 kid = kid->GetNextSibling()) {
179 nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
180 if (SVGFrame) {
181 NS_ABORT_IF_FALSE(!(kid->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
182 "Check for this explicitly in the |if|, then");
183 bool isFirstReflow = (kid->GetStateBits() & NS_FRAME_FIRST_REFLOW);
184 // Remove bits so that ScheduleBoundsUpdate will work:
185 kid->RemoveStateBits(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
186 NS_FRAME_HAS_DIRTY_CHILDREN);
187 // No need to invalidate the new kid's old bounds, so we just use
188 // nsSVGUtils::ScheduleBoundsUpdate.
189 nsSVGUtils::ScheduleReflowSVG(kid);
190 if (isFirstReflow) {
191 // Add back the NS_FRAME_FIRST_REFLOW bit:
192 kid->AddStateBits(NS_FRAME_FIRST_REFLOW);
193 }
194 }
195 }
196 }
198 return NS_OK;
199 }
201 nsresult
202 nsSVGDisplayContainerFrame::RemoveFrame(ChildListID aListID,
203 nsIFrame* aOldFrame)
204 {
205 nsSVGEffects::InvalidateRenderingObservers(aOldFrame);
207 // nsSVGContainerFrame::RemoveFrame doesn't call down into
208 // nsContainerFrame::RemoveFrame, so it doesn't call FrameNeedsReflow. We
209 // need to schedule a repaint and schedule an update to our overflow rects.
210 SchedulePaint();
211 PresContext()->RestyleManager()->PostRestyleEvent(
212 mContent->AsElement(), nsRestyleHint(0), nsChangeHint_UpdateOverflow);
214 nsresult rv = nsSVGContainerFrame::RemoveFrame(aListID, aOldFrame);
216 if (!(GetStateBits() & (NS_FRAME_IS_NONDISPLAY | NS_STATE_IS_OUTER_SVG))) {
217 nsSVGUtils::NotifyAncestorsOfFilterRegionChange(this);
218 }
220 return rv;
221 }
223 bool
224 nsSVGDisplayContainerFrame::IsSVGTransformed(gfx::Matrix *aOwnTransform,
225 gfx::Matrix *aFromParentTransform) const
226 {
227 bool foundTransform = false;
229 // Check if our parent has children-only transforms:
230 nsIFrame *parent = GetParent();
231 if (parent &&
232 parent->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
233 foundTransform = static_cast<nsSVGContainerFrame*>(parent)->
234 HasChildrenOnlyTransform(aFromParentTransform);
235 }
237 // mContent could be a XUL element so check for an SVG element before casting
238 if (mContent->IsSVG()) {
239 nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
240 nsSVGAnimatedTransformList* transformList =
241 content->GetAnimatedTransformList();
242 if ((transformList && transformList->HasTransform()) ||
243 content->GetAnimateMotionTransform()) {
244 if (aOwnTransform) {
245 *aOwnTransform = gfx::ToMatrix(content->PrependLocalTransformsTo(gfxMatrix(),
246 nsSVGElement::eUserSpaceToParent));
247 }
248 foundTransform = true;
249 }
250 }
251 return foundTransform;
252 }
254 //----------------------------------------------------------------------
255 // nsISVGChildFrame methods
257 nsresult
258 nsSVGDisplayContainerFrame::PaintSVG(nsRenderingContext* aContext,
259 const nsIntRect *aDirtyRect,
260 nsIFrame* aTransformRoot)
261 {
262 NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
263 (mState & NS_FRAME_IS_NONDISPLAY) ||
264 PresContext()->IsGlyph(),
265 "If display lists are enabled, only painting of non-display "
266 "SVG should take this code path");
268 const nsStyleDisplay *display = StyleDisplay();
269 if (display->mOpacity == 0.0)
270 return NS_OK;
272 for (nsIFrame* kid = mFrames.FirstChild(); kid;
273 kid = kid->GetNextSibling()) {
274 nsSVGUtils::PaintFrameWithEffects(aContext, aDirtyRect, kid, aTransformRoot);
275 }
277 return NS_OK;
278 }
280 nsIFrame*
281 nsSVGDisplayContainerFrame::GetFrameForPoint(const nsPoint &aPoint)
282 {
283 NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() ||
284 (mState & NS_FRAME_IS_NONDISPLAY),
285 "If display lists are enabled, only hit-testing of a "
286 "clipPath's contents should take this code path");
287 return nsSVGUtils::HitTestChildren(this, aPoint);
288 }
290 nsRect
291 nsSVGDisplayContainerFrame::GetCoveredRegion()
292 {
293 return nsSVGUtils::GetCoveredRegion(mFrames);
294 }
296 void
297 nsSVGDisplayContainerFrame::ReflowSVG()
298 {
299 NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
300 "This call is probably a wasteful mistake");
302 NS_ABORT_IF_FALSE(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
303 "ReflowSVG mechanism not designed for this");
305 NS_ABORT_IF_FALSE(GetType() != nsGkAtoms::svgOuterSVGFrame,
306 "Do not call on outer-<svg>");
308 if (!nsSVGUtils::NeedsReflowSVG(this)) {
309 return;
310 }
312 // If the NS_FRAME_FIRST_REFLOW bit has been removed from our parent frame,
313 // then our outer-<svg> has previously had its initial reflow. In that case
314 // we need to make sure that that bit has been removed from ourself _before_
315 // recursing over our children to ensure that they know too. Otherwise, we
316 // need to remove it _after_ recursing over our children so that they know
317 // the initial reflow is currently underway.
319 bool isFirstReflow = (mState & NS_FRAME_FIRST_REFLOW);
321 bool outerSVGHasHadFirstReflow =
322 (GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) == 0;
324 if (outerSVGHasHadFirstReflow) {
325 mState &= ~NS_FRAME_FIRST_REFLOW; // tell our children
326 }
328 nsOverflowAreas overflowRects;
330 for (nsIFrame* kid = mFrames.FirstChild(); kid;
331 kid = kid->GetNextSibling()) {
332 nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
333 if (SVGFrame) {
334 NS_ABORT_IF_FALSE(!(kid->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
335 "Check for this explicitly in the |if|, then");
336 kid->AddStateBits(mState & NS_FRAME_IS_DIRTY);
337 SVGFrame->ReflowSVG();
339 // We build up our child frame overflows here instead of using
340 // nsLayoutUtils::UnionChildOverflow since SVG frame's all use the same
341 // frame list, and we're iterating over that list now anyway.
342 ConsiderChildOverflow(overflowRects, kid);
343 } else {
344 // Inside a non-display container frame, we might have some
345 // SVGTextFrames. We need to cause those to get reflowed in
346 // case they are the target of a rendering observer.
347 NS_ASSERTION(kid->GetStateBits() & NS_FRAME_IS_NONDISPLAY,
348 "expected kid to be a NS_FRAME_IS_NONDISPLAY frame");
349 if (kid->GetStateBits() & NS_FRAME_IS_DIRTY) {
350 nsSVGContainerFrame* container = do_QueryFrame(kid);
351 if (container && container->GetContent()->IsSVG()) {
352 ReflowSVGNonDisplayText(container);
353 }
354 }
355 }
356 }
358 // <svg> can create an SVG viewport with an offset due to its
359 // x/y/width/height attributes, and <use> can introduce an offset with an
360 // empty mRect (any width/height is copied to an anonymous <svg> child).
361 // Other than that containers should not set mRect since all other offsets
362 // come from transforms, which are accounted for by nsDisplayTransform.
363 // Note that we rely on |overflow:visible| to allow display list items to be
364 // created for our children.
365 NS_ABORT_IF_FALSE(mContent->Tag() == nsGkAtoms::svg ||
366 (mContent->Tag() == nsGkAtoms::use &&
367 mRect.Size() == nsSize(0,0)) ||
368 mRect.IsEqualEdges(nsRect()),
369 "Only inner-<svg>/<use> is expected to have mRect set");
371 if (isFirstReflow) {
372 // Make sure we have our filter property (if any) before calling
373 // FinishAndStoreOverflow (subsequent filter changes are handled off
374 // nsChangeHint_UpdateEffects):
375 nsSVGEffects::UpdateEffects(this);
376 }
378 FinishAndStoreOverflow(overflowRects, mRect.Size());
380 // Remove state bits after FinishAndStoreOverflow so that it doesn't
381 // invalidate on first reflow:
382 mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
383 NS_FRAME_HAS_DIRTY_CHILDREN);
384 }
386 void
387 nsSVGDisplayContainerFrame::NotifySVGChanged(uint32_t aFlags)
388 {
389 NS_ABORT_IF_FALSE(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
390 "Invalidation logic may need adjusting");
392 nsSVGUtils::NotifyChildrenOfSVGChange(this, aFlags);
393 }
395 SVGBBox
396 nsSVGDisplayContainerFrame::GetBBoxContribution(
397 const Matrix &aToBBoxUserspace,
398 uint32_t aFlags)
399 {
400 SVGBBox bboxUnion;
402 nsIFrame* kid = mFrames.FirstChild();
403 while (kid) {
404 nsIContent *content = kid->GetContent();
405 nsISVGChildFrame* svgKid = do_QueryFrame(kid);
406 // content could be a XUL element so check for an SVG element before casting
407 if (svgKid && (!content->IsSVG() ||
408 static_cast<const nsSVGElement*>(content)->HasValidDimensions())) {
410 gfxMatrix transform = gfx::ThebesMatrix(aToBBoxUserspace);
411 if (content->IsSVG()) {
412 transform = static_cast<nsSVGElement*>(content)->
413 PrependLocalTransformsTo(transform);
414 }
415 // We need to include zero width/height vertical/horizontal lines, so we have
416 // to use UnionEdges.
417 bboxUnion.UnionEdges(svgKid->GetBBoxContribution(gfx::ToMatrix(transform), aFlags));
418 }
419 kid = kid->GetNextSibling();
420 }
422 return bboxUnion;
423 }