layout/svg/nsSVGForeignObjectFrame.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 "nsSVGForeignObjectFrame.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 "nsNameSpaceManager.h"
michael@0 13 #include "nsLayoutUtils.h"
michael@0 14 #include "nsRegion.h"
michael@0 15 #include "nsRenderingContext.h"
michael@0 16 #include "nsSVGContainerFrame.h"
michael@0 17 #include "nsSVGEffects.h"
michael@0 18 #include "mozilla/dom/SVGForeignObjectElement.h"
michael@0 19 #include "nsSVGIntegrationUtils.h"
michael@0 20 #include "nsSVGOuterSVGFrame.h"
michael@0 21 #include "nsSVGUtils.h"
michael@0 22 #include "mozilla/AutoRestore.h"
michael@0 23
michael@0 24 using namespace mozilla;
michael@0 25 using namespace mozilla::dom;
michael@0 26
michael@0 27 //----------------------------------------------------------------------
michael@0 28 // Implementation
michael@0 29
michael@0 30 nsIFrame*
michael@0 31 NS_NewSVGForeignObjectFrame(nsIPresShell *aPresShell,
michael@0 32 nsStyleContext *aContext)
michael@0 33 {
michael@0 34 return new (aPresShell) nsSVGForeignObjectFrame(aContext);
michael@0 35 }
michael@0 36
michael@0 37 NS_IMPL_FRAMEARENA_HELPERS(nsSVGForeignObjectFrame)
michael@0 38
michael@0 39 nsSVGForeignObjectFrame::nsSVGForeignObjectFrame(nsStyleContext* aContext)
michael@0 40 : nsSVGForeignObjectFrameBase(aContext),
michael@0 41 mInReflow(false)
michael@0 42 {
michael@0 43 AddStateBits(NS_FRAME_REFLOW_ROOT | NS_FRAME_MAY_BE_TRANSFORMED |
michael@0 44 NS_FRAME_SVG_LAYOUT);
michael@0 45 }
michael@0 46
michael@0 47 //----------------------------------------------------------------------
michael@0 48 // nsIFrame methods
michael@0 49
michael@0 50 NS_QUERYFRAME_HEAD(nsSVGForeignObjectFrame)
michael@0 51 NS_QUERYFRAME_ENTRY(nsISVGChildFrame)
michael@0 52 NS_QUERYFRAME_TAIL_INHERITING(nsSVGForeignObjectFrameBase)
michael@0 53
michael@0 54 void
michael@0 55 nsSVGForeignObjectFrame::Init(nsIContent* aContent,
michael@0 56 nsIFrame* aParent,
michael@0 57 nsIFrame* aPrevInFlow)
michael@0 58 {
michael@0 59 NS_ASSERTION(aContent->IsSVG(nsGkAtoms::foreignObject),
michael@0 60 "Content is not an SVG foreignObject!");
michael@0 61
michael@0 62 nsSVGForeignObjectFrameBase::Init(aContent, aParent, aPrevInFlow);
michael@0 63 AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD);
michael@0 64 AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER |
michael@0 65 NS_FRAME_FONT_INFLATION_FLOW_ROOT);
michael@0 66 if (!(mState & NS_FRAME_IS_NONDISPLAY)) {
michael@0 67 nsSVGUtils::GetOuterSVGFrame(this)->RegisterForeignObject(this);
michael@0 68 }
michael@0 69 }
michael@0 70
michael@0 71 void nsSVGForeignObjectFrame::DestroyFrom(nsIFrame* aDestructRoot)
michael@0 72 {
michael@0 73 // Only unregister if we registered in the first place:
michael@0 74 if (!(mState & NS_FRAME_IS_NONDISPLAY)) {
michael@0 75 nsSVGUtils::GetOuterSVGFrame(this)->UnregisterForeignObject(this);
michael@0 76 }
michael@0 77 nsSVGForeignObjectFrameBase::DestroyFrom(aDestructRoot);
michael@0 78 }
michael@0 79
michael@0 80 nsIAtom *
michael@0 81 nsSVGForeignObjectFrame::GetType() const
michael@0 82 {
michael@0 83 return nsGkAtoms::svgForeignObjectFrame;
michael@0 84 }
michael@0 85
michael@0 86 nsresult
michael@0 87 nsSVGForeignObjectFrame::AttributeChanged(int32_t aNameSpaceID,
michael@0 88 nsIAtom *aAttribute,
michael@0 89 int32_t aModType)
michael@0 90 {
michael@0 91 if (aNameSpaceID == kNameSpaceID_None) {
michael@0 92 if (aAttribute == nsGkAtoms::width ||
michael@0 93 aAttribute == nsGkAtoms::height) {
michael@0 94 nsSVGEffects::InvalidateRenderingObservers(this);
michael@0 95 nsSVGUtils::ScheduleReflowSVG(this);
michael@0 96 // XXXjwatt: why mark intrinsic widths dirty? can't we just use eResize?
michael@0 97 RequestReflow(nsIPresShell::eStyleChange);
michael@0 98 } else if (aAttribute == nsGkAtoms::x ||
michael@0 99 aAttribute == nsGkAtoms::y) {
michael@0 100 // make sure our cached transform matrix gets (lazily) updated
michael@0 101 mCanvasTM = nullptr;
michael@0 102 nsSVGEffects::InvalidateRenderingObservers(this);
michael@0 103 nsSVGUtils::ScheduleReflowSVG(this);
michael@0 104 } else if (aAttribute == nsGkAtoms::transform) {
michael@0 105 // We don't invalidate for transform changes (the layers code does that).
michael@0 106 // Also note that SVGTransformableElement::GetAttributeChangeHint will
michael@0 107 // return nsChangeHint_UpdateOverflow for "transform" attribute changes
michael@0 108 // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call.
michael@0 109 mCanvasTM = nullptr;
michael@0 110 } else if (aAttribute == nsGkAtoms::viewBox ||
michael@0 111 aAttribute == nsGkAtoms::preserveAspectRatio) {
michael@0 112 nsSVGEffects::InvalidateRenderingObservers(this);
michael@0 113 }
michael@0 114 }
michael@0 115
michael@0 116 return NS_OK;
michael@0 117 }
michael@0 118
michael@0 119 nsresult
michael@0 120 nsSVGForeignObjectFrame::Reflow(nsPresContext* aPresContext,
michael@0 121 nsHTMLReflowMetrics& aDesiredSize,
michael@0 122 const nsHTMLReflowState& aReflowState,
michael@0 123 nsReflowStatus& aStatus)
michael@0 124 {
michael@0 125 NS_ABORT_IF_FALSE(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
michael@0 126 "Should not have been called");
michael@0 127
michael@0 128 // Only InvalidateAndScheduleBoundsUpdate marks us with NS_FRAME_IS_DIRTY,
michael@0 129 // so if that bit is still set we still have a resize pending. If we hit
michael@0 130 // this assertion, then we should get the presShell to skip reflow roots
michael@0 131 // that have a dirty parent since a reflow is going to come via the
michael@0 132 // reflow root's parent anyway.
michael@0 133 NS_ASSERTION(!(GetStateBits() & NS_FRAME_IS_DIRTY),
michael@0 134 "Reflowing while a resize is pending is wasteful");
michael@0 135
michael@0 136 // ReflowSVG makes sure mRect is up to date before we're called.
michael@0 137
michael@0 138 NS_ASSERTION(!aReflowState.parentReflowState,
michael@0 139 "should only get reflow from being reflow root");
michael@0 140 NS_ASSERTION(aReflowState.ComputedWidth() == GetSize().width &&
michael@0 141 aReflowState.ComputedHeight() == GetSize().height,
michael@0 142 "reflow roots should be reflowed at existing size and "
michael@0 143 "svg.css should ensure we have no padding/border/margin");
michael@0 144
michael@0 145 DoReflow();
michael@0 146
michael@0 147 aDesiredSize.Width() = aReflowState.ComputedWidth();
michael@0 148 aDesiredSize.Height() = aReflowState.ComputedHeight();
michael@0 149 aDesiredSize.SetOverflowAreasToDesiredBounds();
michael@0 150 aStatus = NS_FRAME_COMPLETE;
michael@0 151
michael@0 152 return NS_OK;
michael@0 153 }
michael@0 154
michael@0 155 void
michael@0 156 nsSVGForeignObjectFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
michael@0 157 const nsRect& aDirtyRect,
michael@0 158 const nsDisplayListSet& aLists)
michael@0 159 {
michael@0 160 if (!static_cast<const nsSVGElement*>(mContent)->HasValidDimensions()) {
michael@0 161 return;
michael@0 162 }
michael@0 163 BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists);
michael@0 164 }
michael@0 165
michael@0 166 bool
michael@0 167 nsSVGForeignObjectFrame::IsSVGTransformed(Matrix *aOwnTransform,
michael@0 168 Matrix *aFromParentTransform) const
michael@0 169 {
michael@0 170 bool foundTransform = false;
michael@0 171
michael@0 172 // Check if our parent has children-only transforms:
michael@0 173 nsIFrame *parent = GetParent();
michael@0 174 if (parent &&
michael@0 175 parent->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
michael@0 176 foundTransform = static_cast<nsSVGContainerFrame*>(parent)->
michael@0 177 HasChildrenOnlyTransform(aFromParentTransform);
michael@0 178 }
michael@0 179
michael@0 180 nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
michael@0 181 nsSVGAnimatedTransformList* transformList =
michael@0 182 content->GetAnimatedTransformList();
michael@0 183 if ((transformList && transformList->HasTransform()) ||
michael@0 184 content->GetAnimateMotionTransform()) {
michael@0 185 if (aOwnTransform) {
michael@0 186 *aOwnTransform = gfx::ToMatrix(content->PrependLocalTransformsTo(gfxMatrix(),
michael@0 187 nsSVGElement::eUserSpaceToParent));
michael@0 188 }
michael@0 189 foundTransform = true;
michael@0 190 }
michael@0 191 return foundTransform;
michael@0 192 }
michael@0 193
michael@0 194 nsresult
michael@0 195 nsSVGForeignObjectFrame::PaintSVG(nsRenderingContext *aContext,
michael@0 196 const nsIntRect *aDirtyRect,
michael@0 197 nsIFrame* aTransformRoot)
michael@0 198 {
michael@0 199 NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
michael@0 200 (mState & NS_FRAME_IS_NONDISPLAY),
michael@0 201 "If display lists are enabled, only painting of non-display "
michael@0 202 "SVG should take this code path");
michael@0 203
michael@0 204 if (IsDisabled())
michael@0 205 return NS_OK;
michael@0 206
michael@0 207 nsIFrame* kid = GetFirstPrincipalChild();
michael@0 208 if (!kid)
michael@0 209 return NS_OK;
michael@0 210
michael@0 211 gfxMatrix canvasTM = GetCanvasTM(FOR_PAINTING, aTransformRoot);
michael@0 212
michael@0 213 if (canvasTM.IsSingular()) {
michael@0 214 NS_WARNING("Can't render foreignObject element!");
michael@0 215 return NS_ERROR_FAILURE;
michael@0 216 }
michael@0 217
michael@0 218 nsRect kidDirtyRect = kid->GetVisualOverflowRect();
michael@0 219
michael@0 220 /* Check if we need to draw anything. */
michael@0 221 if (aDirtyRect) {
michael@0 222 NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
michael@0 223 (mState & NS_FRAME_IS_NONDISPLAY),
michael@0 224 "Display lists handle dirty rect intersection test");
michael@0 225 // Transform the dirty rect into app units in our userspace.
michael@0 226 gfxMatrix invmatrix = canvasTM;
michael@0 227 invmatrix.Invert();
michael@0 228 NS_ASSERTION(!invmatrix.IsSingular(),
michael@0 229 "inverse of non-singular matrix should be non-singular");
michael@0 230
michael@0 231 gfxRect transDirtyRect = gfxRect(aDirtyRect->x, aDirtyRect->y,
michael@0 232 aDirtyRect->width, aDirtyRect->height);
michael@0 233 transDirtyRect = invmatrix.TransformBounds(transDirtyRect);
michael@0 234
michael@0 235 kidDirtyRect.IntersectRect(kidDirtyRect,
michael@0 236 nsLayoutUtils::RoundGfxRectToAppRect(transDirtyRect,
michael@0 237 PresContext()->AppUnitsPerCSSPixel()));
michael@0 238
michael@0 239 // XXX after bug 614732 is fixed, we will compare mRect with aDirtyRect,
michael@0 240 // not with kidDirtyRect. I.e.
michael@0 241 // int32_t appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel();
michael@0 242 // mRect.ToOutsidePixels(appUnitsPerDevPx).Intersects(*aDirtyRect)
michael@0 243 if (kidDirtyRect.IsEmpty())
michael@0 244 return NS_OK;
michael@0 245 }
michael@0 246
michael@0 247 gfxContext *gfx = aContext->ThebesContext();
michael@0 248
michael@0 249 gfx->Save();
michael@0 250
michael@0 251 if (StyleDisplay()->IsScrollableOverflow()) {
michael@0 252 float x, y, width, height;
michael@0 253 static_cast<nsSVGElement*>(mContent)->
michael@0 254 GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
michael@0 255
michael@0 256 gfxRect clipRect =
michael@0 257 nsSVGUtils::GetClipRectForFrame(this, 0.0f, 0.0f, width, height);
michael@0 258 nsSVGUtils::SetClipRect(gfx, canvasTM, clipRect);
michael@0 259 }
michael@0 260
michael@0 261 // SVG paints in CSS px, but normally frames paint in dev pixels. Here we
michael@0 262 // multiply a CSS-px-to-dev-pixel factor onto canvasTM so our children paint
michael@0 263 // correctly.
michael@0 264 float cssPxPerDevPx = PresContext()->
michael@0 265 AppUnitsToFloatCSSPixels(PresContext()->AppUnitsPerDevPixel());
michael@0 266 gfxMatrix canvasTMForChildren = canvasTM;
michael@0 267 canvasTMForChildren.Scale(cssPxPerDevPx, cssPxPerDevPx);
michael@0 268
michael@0 269 gfx->Multiply(canvasTMForChildren);
michael@0 270
michael@0 271 uint32_t flags = nsLayoutUtils::PAINT_IN_TRANSFORM;
michael@0 272 if (SVGAutoRenderState::IsPaintingToWindow(aContext)) {
michael@0 273 flags |= nsLayoutUtils::PAINT_TO_WINDOW;
michael@0 274 }
michael@0 275 nsresult rv = nsLayoutUtils::PaintFrame(aContext, kid, nsRegion(kidDirtyRect),
michael@0 276 NS_RGBA(0,0,0,0), flags);
michael@0 277
michael@0 278 gfx->Restore();
michael@0 279
michael@0 280 return rv;
michael@0 281 }
michael@0 282
michael@0 283 nsIFrame*
michael@0 284 nsSVGForeignObjectFrame::GetFrameForPoint(const nsPoint &aPoint)
michael@0 285 {
michael@0 286 NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() ||
michael@0 287 (mState & NS_FRAME_IS_NONDISPLAY),
michael@0 288 "If display lists are enabled, only hit-testing of a "
michael@0 289 "clipPath's contents should take this code path");
michael@0 290
michael@0 291 if (IsDisabled() || (GetStateBits() & NS_FRAME_IS_NONDISPLAY))
michael@0 292 return nullptr;
michael@0 293
michael@0 294 nsIFrame* kid = GetFirstPrincipalChild();
michael@0 295 if (!kid)
michael@0 296 return nullptr;
michael@0 297
michael@0 298 float x, y, width, height;
michael@0 299 static_cast<nsSVGElement*>(mContent)->
michael@0 300 GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
michael@0 301
michael@0 302 gfxMatrix tm = GetCanvasTM(FOR_HIT_TESTING).Invert();
michael@0 303 if (tm.IsSingular())
michael@0 304 return nullptr;
michael@0 305
michael@0 306 // Convert aPoint from app units in canvas space to user space:
michael@0 307
michael@0 308 gfxPoint pt = gfxPoint(aPoint.x, aPoint.y) / PresContext()->AppUnitsPerDevPixel();
michael@0 309 pt = tm.Transform(pt);
michael@0 310
michael@0 311 if (!gfxRect(0.0f, 0.0f, width, height).Contains(pt))
michael@0 312 return nullptr;
michael@0 313
michael@0 314 // Convert pt to app units in *local* space:
michael@0 315
michael@0 316 pt = pt * nsPresContext::AppUnitsPerCSSPixel();
michael@0 317 nsPoint point = nsPoint(NSToIntRound(pt.x), NSToIntRound(pt.y));
michael@0 318
michael@0 319 nsIFrame *frame = nsLayoutUtils::GetFrameForPoint(kid, point);
michael@0 320 if (frame && nsSVGUtils::HitTestClip(this, aPoint))
michael@0 321 return frame;
michael@0 322
michael@0 323 return nullptr;
michael@0 324 }
michael@0 325
michael@0 326 nsRect
michael@0 327 nsSVGForeignObjectFrame::GetCoveredRegion()
michael@0 328 {
michael@0 329 float x, y, w, h;
michael@0 330 static_cast<SVGForeignObjectElement*>(mContent)->
michael@0 331 GetAnimatedLengthValues(&x, &y, &w, &h, nullptr);
michael@0 332 if (w < 0.0f) w = 0.0f;
michael@0 333 if (h < 0.0f) h = 0.0f;
michael@0 334 // GetCanvasTM includes the x,y translation
michael@0 335 return nsSVGUtils::ToCanvasBounds(gfxRect(0.0, 0.0, w, h),
michael@0 336 GetCanvasTM(FOR_OUTERSVG_TM),
michael@0 337 PresContext());
michael@0 338 }
michael@0 339
michael@0 340 void
michael@0 341 nsSVGForeignObjectFrame::ReflowSVG()
michael@0 342 {
michael@0 343 NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
michael@0 344 "This call is probably a wasteful mistake");
michael@0 345
michael@0 346 NS_ABORT_IF_FALSE(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
michael@0 347 "ReflowSVG mechanism not designed for this");
michael@0 348
michael@0 349 if (!nsSVGUtils::NeedsReflowSVG(this)) {
michael@0 350 return;
michael@0 351 }
michael@0 352
michael@0 353 // We update mRect before the DoReflow call so that DoReflow uses the
michael@0 354 // correct dimensions:
michael@0 355
michael@0 356 float x, y, w, h;
michael@0 357 static_cast<SVGForeignObjectElement*>(mContent)->
michael@0 358 GetAnimatedLengthValues(&x, &y, &w, &h, nullptr);
michael@0 359
michael@0 360 // If mRect's width or height are negative, reflow blows up! We must clamp!
michael@0 361 if (w < 0.0f) w = 0.0f;
michael@0 362 if (h < 0.0f) h = 0.0f;
michael@0 363
michael@0 364 mRect = nsLayoutUtils::RoundGfxRectToAppRect(
michael@0 365 gfxRect(x, y, w, h),
michael@0 366 PresContext()->AppUnitsPerCSSPixel());
michael@0 367
michael@0 368 // Fully mark our kid dirty so that it gets resized if necessary
michael@0 369 // (NS_FRAME_HAS_DIRTY_CHILDREN isn't enough in that case):
michael@0 370 nsIFrame* kid = GetFirstPrincipalChild();
michael@0 371 kid->AddStateBits(NS_FRAME_IS_DIRTY);
michael@0 372
michael@0 373 // Make sure to not allow interrupts if we're not being reflown as a root:
michael@0 374 nsPresContext::InterruptPreventer noInterrupts(PresContext());
michael@0 375
michael@0 376 DoReflow();
michael@0 377
michael@0 378 if (mState & NS_FRAME_FIRST_REFLOW) {
michael@0 379 // Make sure we have our filter property (if any) before calling
michael@0 380 // FinishAndStoreOverflow (subsequent filter changes are handled off
michael@0 381 // nsChangeHint_UpdateEffects):
michael@0 382 nsSVGEffects::UpdateEffects(this);
michael@0 383 }
michael@0 384
michael@0 385 // If we have a filter, we need to invalidate ourselves because filter
michael@0 386 // output can change even if none of our descendants need repainting.
michael@0 387 if (StyleSVGReset()->HasFilters()) {
michael@0 388 InvalidateFrame();
michael@0 389 }
michael@0 390
michael@0 391 // TODO: once we support |overflow:visible| on foreignObject, then we will
michael@0 392 // need to take account of our descendants here.
michael@0 393 nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
michael@0 394 nsOverflowAreas overflowAreas(overflow, overflow);
michael@0 395 FinishAndStoreOverflow(overflowAreas, mRect.Size());
michael@0 396
michael@0 397 // Now unset the various reflow bits:
michael@0 398 mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
michael@0 399 NS_FRAME_HAS_DIRTY_CHILDREN);
michael@0 400 }
michael@0 401
michael@0 402 void
michael@0 403 nsSVGForeignObjectFrame::NotifySVGChanged(uint32_t aFlags)
michael@0 404 {
michael@0 405 NS_ABORT_IF_FALSE(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
michael@0 406 "Invalidation logic may need adjusting");
michael@0 407
michael@0 408 bool needNewBounds = false; // i.e. mRect or visual overflow rect
michael@0 409 bool needReflow = false;
michael@0 410 bool needNewCanvasTM = false;
michael@0 411
michael@0 412 if (aFlags & COORD_CONTEXT_CHANGED) {
michael@0 413 SVGForeignObjectElement *fO =
michael@0 414 static_cast<SVGForeignObjectElement*>(mContent);
michael@0 415 // Coordinate context changes affect mCanvasTM if we have a
michael@0 416 // percentage 'x' or 'y'
michael@0 417 if (fO->mLengthAttributes[SVGForeignObjectElement::ATTR_X].IsPercentage() ||
michael@0 418 fO->mLengthAttributes[SVGForeignObjectElement::ATTR_Y].IsPercentage()) {
michael@0 419 needNewBounds = true;
michael@0 420 needNewCanvasTM = true;
michael@0 421 }
michael@0 422 // Our coordinate context's width/height has changed. If we have a
michael@0 423 // percentage width/height our dimensions will change so we must reflow.
michael@0 424 if (fO->mLengthAttributes[SVGForeignObjectElement::ATTR_WIDTH].IsPercentage() ||
michael@0 425 fO->mLengthAttributes[SVGForeignObjectElement::ATTR_HEIGHT].IsPercentage()) {
michael@0 426 needNewBounds = true;
michael@0 427 needReflow = true;
michael@0 428 }
michael@0 429 }
michael@0 430
michael@0 431 if (aFlags & TRANSFORM_CHANGED) {
michael@0 432 if (mCanvasTM && mCanvasTM->IsSingular()) {
michael@0 433 needNewBounds = true; // old bounds are bogus
michael@0 434 }
michael@0 435 needNewCanvasTM = true;
michael@0 436 // In an ideal world we would reflow when our CTM changes. This is because
michael@0 437 // glyph metrics do not necessarily scale uniformly with change in scale
michael@0 438 // and, as a result, CTM changes may require text to break at different
michael@0 439 // points. The problem would be how to keep performance acceptable when
michael@0 440 // e.g. the transform of an ancestor is animated.
michael@0 441 // We also seem to get some sort of infinite loop post bug 421584 if we
michael@0 442 // reflow.
michael@0 443 }
michael@0 444
michael@0 445 if (needNewBounds) {
michael@0 446 // Ancestor changes can't affect how we render from the perspective of
michael@0 447 // any rendering observers that we may have, so we don't need to
michael@0 448 // invalidate them. We also don't need to invalidate ourself, since our
michael@0 449 // changed ancestor will have invalidated its entire area, which includes
michael@0 450 // our area.
michael@0 451 nsSVGUtils::ScheduleReflowSVG(this);
michael@0 452 }
michael@0 453
michael@0 454 // If we're called while the PresShell is handling reflow events then we
michael@0 455 // must have been called as a result of the NotifyViewportChange() call in
michael@0 456 // our nsSVGOuterSVGFrame's Reflow() method. We must not call RequestReflow
michael@0 457 // at this point (i.e. during reflow) because it could confuse the
michael@0 458 // PresShell and prevent it from reflowing us properly in future. Besides
michael@0 459 // that, nsSVGOuterSVGFrame::DidReflow will take care of reflowing us
michael@0 460 // synchronously, so there's no need.
michael@0 461 if (needReflow && !PresContext()->PresShell()->IsReflowLocked()) {
michael@0 462 RequestReflow(nsIPresShell::eResize);
michael@0 463 }
michael@0 464
michael@0 465 if (needNewCanvasTM) {
michael@0 466 // Do this after calling InvalidateAndScheduleBoundsUpdate in case we
michael@0 467 // change the code and it needs to use it.
michael@0 468 mCanvasTM = nullptr;
michael@0 469 }
michael@0 470 }
michael@0 471
michael@0 472 SVGBBox
michael@0 473 nsSVGForeignObjectFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace,
michael@0 474 uint32_t aFlags)
michael@0 475 {
michael@0 476 SVGForeignObjectElement *content =
michael@0 477 static_cast<SVGForeignObjectElement*>(mContent);
michael@0 478
michael@0 479 float x, y, w, h;
michael@0 480 content->GetAnimatedLengthValues(&x, &y, &w, &h, nullptr);
michael@0 481
michael@0 482 if (w < 0.0f) w = 0.0f;
michael@0 483 if (h < 0.0f) h = 0.0f;
michael@0 484
michael@0 485 if (aToBBoxUserspace.IsSingular()) {
michael@0 486 // XXX ReportToConsole
michael@0 487 return SVGBBox();
michael@0 488 }
michael@0 489 return aToBBoxUserspace.TransformBounds(gfx::Rect(0.0, 0.0, w, h));
michael@0 490 }
michael@0 491
michael@0 492 //----------------------------------------------------------------------
michael@0 493
michael@0 494 gfxMatrix
michael@0 495 nsSVGForeignObjectFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot)
michael@0 496 {
michael@0 497 if (!(GetStateBits() & NS_FRAME_IS_NONDISPLAY) && !aTransformRoot) {
michael@0 498 if ((aFor == FOR_PAINTING && NS_SVGDisplayListPaintingEnabled()) ||
michael@0 499 (aFor == FOR_HIT_TESTING && NS_SVGDisplayListHitTestingEnabled())) {
michael@0 500 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(this);
michael@0 501 }
michael@0 502 }
michael@0 503 if (!mCanvasTM) {
michael@0 504 NS_ASSERTION(mParent, "null parent");
michael@0 505
michael@0 506 nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(mParent);
michael@0 507 SVGForeignObjectElement *content =
michael@0 508 static_cast<SVGForeignObjectElement*>(mContent);
michael@0 509
michael@0 510 gfxMatrix tm = content->PrependLocalTransformsTo(
michael@0 511 this == aTransformRoot ? gfxMatrix() :
michael@0 512 parent->GetCanvasTM(aFor, aTransformRoot));
michael@0 513
michael@0 514 mCanvasTM = new gfxMatrix(tm);
michael@0 515 }
michael@0 516 return *mCanvasTM;
michael@0 517 }
michael@0 518
michael@0 519 //----------------------------------------------------------------------
michael@0 520 // Implementation helpers
michael@0 521
michael@0 522 void nsSVGForeignObjectFrame::RequestReflow(nsIPresShell::IntrinsicDirty aType)
michael@0 523 {
michael@0 524 if (GetStateBits() & NS_FRAME_FIRST_REFLOW)
michael@0 525 // If we haven't had a ReflowSVG() yet, nothing to do.
michael@0 526 return;
michael@0 527
michael@0 528 nsIFrame* kid = GetFirstPrincipalChild();
michael@0 529 if (!kid)
michael@0 530 return;
michael@0 531
michael@0 532 PresContext()->PresShell()->FrameNeedsReflow(kid, aType, NS_FRAME_IS_DIRTY);
michael@0 533 }
michael@0 534
michael@0 535 void
michael@0 536 nsSVGForeignObjectFrame::DoReflow()
michael@0 537 {
michael@0 538 // Skip reflow if we're zero-sized, unless this is our first reflow.
michael@0 539 if (IsDisabled() &&
michael@0 540 !(GetStateBits() & NS_FRAME_FIRST_REFLOW))
michael@0 541 return;
michael@0 542
michael@0 543 nsPresContext *presContext = PresContext();
michael@0 544 nsIFrame* kid = GetFirstPrincipalChild();
michael@0 545 if (!kid)
michael@0 546 return;
michael@0 547
michael@0 548 // initiate a synchronous reflow here and now:
michael@0 549 nsRefPtr<nsRenderingContext> renderingContext =
michael@0 550 presContext->PresShell()->CreateReferenceRenderingContext();
michael@0 551
michael@0 552 mInReflow = true;
michael@0 553
michael@0 554 nsHTMLReflowState reflowState(presContext, kid,
michael@0 555 renderingContext,
michael@0 556 nsSize(mRect.width, NS_UNCONSTRAINEDSIZE));
michael@0 557 nsHTMLReflowMetrics desiredSize(reflowState);
michael@0 558 nsReflowStatus status;
michael@0 559
michael@0 560 // We don't use mRect.height above because that tells the child to do
michael@0 561 // page/column breaking at that height.
michael@0 562 NS_ASSERTION(reflowState.ComputedPhysicalBorderPadding() == nsMargin(0, 0, 0, 0) &&
michael@0 563 reflowState.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0),
michael@0 564 "style system should ensure that :-moz-svg-foreign-content "
michael@0 565 "does not get styled");
michael@0 566 NS_ASSERTION(reflowState.ComputedWidth() == mRect.width,
michael@0 567 "reflow state made child wrong size");
michael@0 568 reflowState.SetComputedHeight(mRect.height);
michael@0 569
michael@0 570 ReflowChild(kid, presContext, desiredSize, reflowState, 0, 0,
michael@0 571 NS_FRAME_NO_MOVE_FRAME, status);
michael@0 572 NS_ASSERTION(mRect.width == desiredSize.Width() &&
michael@0 573 mRect.height == desiredSize.Height(), "unexpected size");
michael@0 574 FinishReflowChild(kid, presContext, desiredSize, &reflowState, 0, 0,
michael@0 575 NS_FRAME_NO_MOVE_FRAME);
michael@0 576
michael@0 577 mInReflow = false;
michael@0 578 }
michael@0 579
michael@0 580 nsRect
michael@0 581 nsSVGForeignObjectFrame::GetInvalidRegion()
michael@0 582 {
michael@0 583 nsIFrame* kid = GetFirstPrincipalChild();
michael@0 584 if (kid->HasInvalidFrameInSubtree()) {
michael@0 585 gfxRect r(mRect.x, mRect.y, mRect.width, mRect.height);
michael@0 586 r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel());
michael@0 587 nsRect rect = nsSVGUtils::ToCanvasBounds(r, GetCanvasTM(FOR_PAINTING), PresContext());
michael@0 588 rect = nsSVGUtils::GetPostFilterVisualOverflowRect(this, rect);
michael@0 589 return rect;
michael@0 590 }
michael@0 591 return nsRect();
michael@0 592 }
michael@0 593
michael@0 594

mercurial