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