layout/svg/nsSVGImageFrame.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 // Keep in (case-insensitive) order:
michael@0 7 #include "gfxContext.h"
michael@0 8 #include "gfxPlatform.h"
michael@0 9 #include "mozilla/gfx/2D.h"
michael@0 10 #include "imgIContainer.h"
michael@0 11 #include "nsIImageLoadingContent.h"
michael@0 12 #include "nsLayoutUtils.h"
michael@0 13 #include "nsRenderingContext.h"
michael@0 14 #include "imgINotificationObserver.h"
michael@0 15 #include "nsSVGEffects.h"
michael@0 16 #include "nsSVGPathGeometryFrame.h"
michael@0 17 #include "mozilla/dom/SVGSVGElement.h"
michael@0 18 #include "nsSVGUtils.h"
michael@0 19 #include "SVGContentUtils.h"
michael@0 20 #include "SVGImageContext.h"
michael@0 21 #include "mozilla/dom/SVGImageElement.h"
michael@0 22 #include "nsContentUtils.h"
michael@0 23 #include "nsIReflowCallback.h"
michael@0 24
michael@0 25 using namespace mozilla;
michael@0 26 using namespace mozilla::dom;
michael@0 27 using namespace mozilla::gfx;
michael@0 28
michael@0 29 class nsSVGImageFrame;
michael@0 30
michael@0 31 class nsSVGImageListener MOZ_FINAL : public imgINotificationObserver
michael@0 32 {
michael@0 33 public:
michael@0 34 nsSVGImageListener(nsSVGImageFrame *aFrame);
michael@0 35
michael@0 36 NS_DECL_ISUPPORTS
michael@0 37 NS_DECL_IMGINOTIFICATIONOBSERVER
michael@0 38
michael@0 39 void SetFrame(nsSVGImageFrame *frame) { mFrame = frame; }
michael@0 40
michael@0 41 private:
michael@0 42 nsSVGImageFrame *mFrame;
michael@0 43 };
michael@0 44
michael@0 45 typedef nsSVGPathGeometryFrame nsSVGImageFrameBase;
michael@0 46
michael@0 47 class nsSVGImageFrame : public nsSVGImageFrameBase,
michael@0 48 public nsIReflowCallback
michael@0 49 {
michael@0 50 friend nsIFrame*
michael@0 51 NS_NewSVGImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
michael@0 52
michael@0 53 protected:
michael@0 54 nsSVGImageFrame(nsStyleContext* aContext) : nsSVGImageFrameBase(aContext),
michael@0 55 mReflowCallbackPosted(false) {}
michael@0 56 virtual ~nsSVGImageFrame();
michael@0 57
michael@0 58 public:
michael@0 59 NS_DECL_FRAMEARENA_HELPERS
michael@0 60
michael@0 61 // nsISVGChildFrame interface:
michael@0 62 virtual nsresult PaintSVG(nsRenderingContext *aContext,
michael@0 63 const nsIntRect *aDirtyRect,
michael@0 64 nsIFrame* aTransformRoot) MOZ_OVERRIDE;
michael@0 65 virtual nsIFrame* GetFrameForPoint(const nsPoint &aPoint) MOZ_OVERRIDE;
michael@0 66 virtual void ReflowSVG() MOZ_OVERRIDE;
michael@0 67
michael@0 68 // nsSVGPathGeometryFrame methods:
michael@0 69 virtual uint16_t GetHitTestFlags() MOZ_OVERRIDE;
michael@0 70
michael@0 71 // nsIFrame interface:
michael@0 72 virtual nsresult AttributeChanged(int32_t aNameSpaceID,
michael@0 73 nsIAtom* aAttribute,
michael@0 74 int32_t aModType) MOZ_OVERRIDE;
michael@0 75 virtual void Init(nsIContent* aContent,
michael@0 76 nsIFrame* aParent,
michael@0 77 nsIFrame* aPrevInFlow) MOZ_OVERRIDE;
michael@0 78 virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE;
michael@0 79
michael@0 80 /**
michael@0 81 * Get the "type" of the frame
michael@0 82 *
michael@0 83 * @see nsGkAtoms::svgImageFrame
michael@0 84 */
michael@0 85 virtual nsIAtom* GetType() const MOZ_OVERRIDE;
michael@0 86
michael@0 87 #ifdef DEBUG_FRAME_DUMP
michael@0 88 virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE
michael@0 89 {
michael@0 90 return MakeFrameName(NS_LITERAL_STRING("SVGImage"), aResult);
michael@0 91 }
michael@0 92 #endif
michael@0 93
michael@0 94 // nsIReflowCallback
michael@0 95 virtual bool ReflowFinished() MOZ_OVERRIDE;
michael@0 96 virtual void ReflowCallbackCanceled() MOZ_OVERRIDE;
michael@0 97
michael@0 98 private:
michael@0 99 gfx::Matrix GetRasterImageTransform(int32_t aNativeWidth,
michael@0 100 int32_t aNativeHeight,
michael@0 101 uint32_t aFor,
michael@0 102 nsIFrame* aTransformRoot = nullptr);
michael@0 103 gfx::Matrix GetVectorImageTransform(uint32_t aFor,
michael@0 104 nsIFrame* aTransformRoot = nullptr);
michael@0 105 bool TransformContextForPainting(gfxContext* aGfxContext,
michael@0 106 nsIFrame* aTransformRoot);
michael@0 107
michael@0 108 nsCOMPtr<imgINotificationObserver> mListener;
michael@0 109
michael@0 110 nsCOMPtr<imgIContainer> mImageContainer;
michael@0 111
michael@0 112 bool mReflowCallbackPosted;
michael@0 113
michael@0 114 friend class nsSVGImageListener;
michael@0 115 };
michael@0 116
michael@0 117 //----------------------------------------------------------------------
michael@0 118 // Implementation
michael@0 119
michael@0 120 nsIFrame*
michael@0 121 NS_NewSVGImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
michael@0 122 {
michael@0 123 return new (aPresShell) nsSVGImageFrame(aContext);
michael@0 124 }
michael@0 125
michael@0 126 NS_IMPL_FRAMEARENA_HELPERS(nsSVGImageFrame)
michael@0 127
michael@0 128 nsSVGImageFrame::~nsSVGImageFrame()
michael@0 129 {
michael@0 130 // set the frame to null so we don't send messages to a dead object.
michael@0 131 if (mListener) {
michael@0 132 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
michael@0 133 if (imageLoader) {
michael@0 134 imageLoader->RemoveObserver(mListener);
michael@0 135 }
michael@0 136 reinterpret_cast<nsSVGImageListener*>(mListener.get())->SetFrame(nullptr);
michael@0 137 }
michael@0 138 mListener = nullptr;
michael@0 139 }
michael@0 140
michael@0 141 void
michael@0 142 nsSVGImageFrame::Init(nsIContent* aContent,
michael@0 143 nsIFrame* aParent,
michael@0 144 nsIFrame* aPrevInFlow)
michael@0 145 {
michael@0 146 NS_ASSERTION(aContent->IsSVG(nsGkAtoms::image),
michael@0 147 "Content is not an SVG image!");
michael@0 148
michael@0 149 nsSVGImageFrameBase::Init(aContent, aParent, aPrevInFlow);
michael@0 150
michael@0 151 mListener = new nsSVGImageListener(this);
michael@0 152 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
michael@0 153 if (!imageLoader) {
michael@0 154 NS_RUNTIMEABORT("Why is this not an image loading content?");
michael@0 155 }
michael@0 156
michael@0 157 // We should have a PresContext now, so let's notify our image loader that
michael@0 158 // we need to register any image animations with the refresh driver.
michael@0 159 imageLoader->FrameCreated(this);
michael@0 160
michael@0 161 imageLoader->AddObserver(mListener);
michael@0 162 }
michael@0 163
michael@0 164 /* virtual */ void
michael@0 165 nsSVGImageFrame::DestroyFrom(nsIFrame* aDestructRoot)
michael@0 166 {
michael@0 167 if (mReflowCallbackPosted) {
michael@0 168 PresContext()->PresShell()->CancelReflowCallback(this);
michael@0 169 mReflowCallbackPosted = false;
michael@0 170 }
michael@0 171
michael@0 172 nsCOMPtr<nsIImageLoadingContent> imageLoader =
michael@0 173 do_QueryInterface(nsFrame::mContent);
michael@0 174
michael@0 175 if (imageLoader) {
michael@0 176 imageLoader->FrameDestroyed(this);
michael@0 177 }
michael@0 178
michael@0 179 nsFrame::DestroyFrom(aDestructRoot);
michael@0 180 }
michael@0 181
michael@0 182 //----------------------------------------------------------------------
michael@0 183 // nsIFrame methods:
michael@0 184
michael@0 185 nsresult
michael@0 186 nsSVGImageFrame::AttributeChanged(int32_t aNameSpaceID,
michael@0 187 nsIAtom* aAttribute,
michael@0 188 int32_t aModType)
michael@0 189 {
michael@0 190 if (aNameSpaceID == kNameSpaceID_None) {
michael@0 191 if (aAttribute == nsGkAtoms::x ||
michael@0 192 aAttribute == nsGkAtoms::y ||
michael@0 193 aAttribute == nsGkAtoms::width ||
michael@0 194 aAttribute == nsGkAtoms::height) {
michael@0 195 nsSVGEffects::InvalidateRenderingObservers(this);
michael@0 196 nsSVGUtils::ScheduleReflowSVG(this);
michael@0 197 return NS_OK;
michael@0 198 }
michael@0 199 else if (aAttribute == nsGkAtoms::preserveAspectRatio) {
michael@0 200 // We don't paint the content of the image using display lists, therefore
michael@0 201 // we have to invalidate for this children-only transform changes since
michael@0 202 // there is no layer tree to notice that the transform changed and
michael@0 203 // recomposite.
michael@0 204 InvalidateFrame();
michael@0 205 return NS_OK;
michael@0 206 }
michael@0 207 }
michael@0 208 if (aNameSpaceID == kNameSpaceID_XLink &&
michael@0 209 aAttribute == nsGkAtoms::href) {
michael@0 210
michael@0 211 // Prevent setting image.src by exiting early
michael@0 212 if (nsContentUtils::IsImageSrcSetDisabled()) {
michael@0 213 return NS_OK;
michael@0 214 }
michael@0 215 SVGImageElement *element = static_cast<SVGImageElement*>(mContent);
michael@0 216
michael@0 217 if (element->mStringAttributes[SVGImageElement::HREF].IsExplicitlySet()) {
michael@0 218 element->LoadSVGImage(true, true);
michael@0 219 } else {
michael@0 220 element->CancelImageRequests(true);
michael@0 221 }
michael@0 222 }
michael@0 223
michael@0 224 return nsSVGImageFrameBase::AttributeChanged(aNameSpaceID,
michael@0 225 aAttribute, aModType);
michael@0 226 }
michael@0 227
michael@0 228 gfx::Matrix
michael@0 229 nsSVGImageFrame::GetRasterImageTransform(int32_t aNativeWidth,
michael@0 230 int32_t aNativeHeight,
michael@0 231 uint32_t aFor,
michael@0 232 nsIFrame* aTransformRoot)
michael@0 233 {
michael@0 234 float x, y, width, height;
michael@0 235 SVGImageElement *element = static_cast<SVGImageElement*>(mContent);
michael@0 236 element->GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
michael@0 237
michael@0 238 Matrix viewBoxTM =
michael@0 239 SVGContentUtils::GetViewBoxTransform(width, height,
michael@0 240 0, 0, aNativeWidth, aNativeHeight,
michael@0 241 element->mPreserveAspectRatio);
michael@0 242
michael@0 243 return viewBoxTM *
michael@0 244 gfx::Matrix::Translation(x, y) *
michael@0 245 gfx::ToMatrix(GetCanvasTM(aFor, aTransformRoot));
michael@0 246 }
michael@0 247
michael@0 248 gfx::Matrix
michael@0 249 nsSVGImageFrame::GetVectorImageTransform(uint32_t aFor,
michael@0 250 nsIFrame* aTransformRoot)
michael@0 251 {
michael@0 252 float x, y, width, height;
michael@0 253 SVGImageElement *element = static_cast<SVGImageElement*>(mContent);
michael@0 254 element->GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
michael@0 255
michael@0 256 // No viewBoxTM needed here -- our height/width overrides any concept of
michael@0 257 // "native size" that the SVG image has, and it will handle viewBox and
michael@0 258 // preserveAspectRatio on its own once we give it a region to draw into.
michael@0 259
michael@0 260 return gfx::Matrix::Translation(x, y) *
michael@0 261 gfx::ToMatrix(GetCanvasTM(aFor, aTransformRoot));
michael@0 262 }
michael@0 263
michael@0 264 bool
michael@0 265 nsSVGImageFrame::TransformContextForPainting(gfxContext* aGfxContext,
michael@0 266 nsIFrame* aTransformRoot)
michael@0 267 {
michael@0 268 gfx::Matrix imageTransform;
michael@0 269 if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) {
michael@0 270 imageTransform = GetVectorImageTransform(FOR_PAINTING, aTransformRoot);
michael@0 271 } else {
michael@0 272 int32_t nativeWidth, nativeHeight;
michael@0 273 if (NS_FAILED(mImageContainer->GetWidth(&nativeWidth)) ||
michael@0 274 NS_FAILED(mImageContainer->GetHeight(&nativeHeight)) ||
michael@0 275 nativeWidth == 0 || nativeHeight == 0) {
michael@0 276 return false;
michael@0 277 }
michael@0 278 imageTransform =
michael@0 279 GetRasterImageTransform(nativeWidth, nativeHeight, FOR_PAINTING,
michael@0 280 aTransformRoot);
michael@0 281
michael@0 282 // NOTE: We need to cancel out the effects of Full-Page-Zoom, or else
michael@0 283 // it'll get applied an extra time by DrawSingleUnscaledImage.
michael@0 284 nscoord appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel();
michael@0 285 gfxFloat pageZoomFactor =
michael@0 286 nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPx);
michael@0 287 imageTransform.Scale(pageZoomFactor, pageZoomFactor);
michael@0 288 }
michael@0 289
michael@0 290 if (imageTransform.IsSingular()) {
michael@0 291 return false;
michael@0 292 }
michael@0 293
michael@0 294 aGfxContext->Multiply(ThebesMatrix(imageTransform));
michael@0 295 return true;
michael@0 296 }
michael@0 297
michael@0 298 //----------------------------------------------------------------------
michael@0 299 // nsISVGChildFrame methods:
michael@0 300 nsresult
michael@0 301 nsSVGImageFrame::PaintSVG(nsRenderingContext *aContext,
michael@0 302 const nsIntRect *aDirtyRect,
michael@0 303 nsIFrame* aTransformRoot)
michael@0 304 {
michael@0 305 nsresult rv = NS_OK;
michael@0 306
michael@0 307 if (!StyleVisibility()->IsVisible())
michael@0 308 return NS_OK;
michael@0 309
michael@0 310 float x, y, width, height;
michael@0 311 SVGImageElement *imgElem = static_cast<SVGImageElement*>(mContent);
michael@0 312 imgElem->GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
michael@0 313 NS_ASSERTION(width > 0 && height > 0,
michael@0 314 "Should only be painting things with valid width/height");
michael@0 315
michael@0 316 if (!mImageContainer) {
michael@0 317 nsCOMPtr<imgIRequest> currentRequest;
michael@0 318 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
michael@0 319 if (imageLoader)
michael@0 320 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
michael@0 321 getter_AddRefs(currentRequest));
michael@0 322
michael@0 323 if (currentRequest)
michael@0 324 currentRequest->GetImage(getter_AddRefs(mImageContainer));
michael@0 325 }
michael@0 326
michael@0 327 if (mImageContainer) {
michael@0 328 gfxContext* ctx = aContext->ThebesContext();
michael@0 329 gfxContextAutoSaveRestore autoRestorer(ctx);
michael@0 330
michael@0 331 if (StyleDisplay()->IsScrollableOverflow()) {
michael@0 332 gfxRect clipRect = nsSVGUtils::GetClipRectForFrame(this, x, y,
michael@0 333 width, height);
michael@0 334 nsSVGUtils::SetClipRect(ctx, GetCanvasTM(FOR_PAINTING, aTransformRoot),
michael@0 335 clipRect);
michael@0 336 }
michael@0 337
michael@0 338 if (!TransformContextForPainting(ctx, aTransformRoot)) {
michael@0 339 return NS_ERROR_FAILURE;
michael@0 340 }
michael@0 341
michael@0 342 // fill-opacity doesn't affect <image>, so if we're allowed to
michael@0 343 // optimize group opacity, the opacity used for compositing the
michael@0 344 // image into the current canvas is just the group opacity.
michael@0 345 float opacity = 1.0f;
michael@0 346 if (nsSVGUtils::CanOptimizeOpacity(this)) {
michael@0 347 opacity = StyleDisplay()->mOpacity;
michael@0 348 }
michael@0 349
michael@0 350 if (opacity != 1.0f || StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
michael@0 351 ctx->PushGroup(gfxContentType::COLOR_ALPHA);
michael@0 352 }
michael@0 353
michael@0 354 nscoord appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel();
michael@0 355 nsRect dirtyRect; // only used if aDirtyRect is non-null
michael@0 356 if (aDirtyRect) {
michael@0 357 NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
michael@0 358 (mState & NS_FRAME_IS_NONDISPLAY),
michael@0 359 "Display lists handle dirty rect intersection test");
michael@0 360 dirtyRect = aDirtyRect->ToAppUnits(appUnitsPerDevPx);
michael@0 361 // Adjust dirtyRect to match our local coordinate system.
michael@0 362 nsRect rootRect =
michael@0 363 nsSVGUtils::TransformFrameRectToOuterSVG(mRect,
michael@0 364 GetCanvasTM(FOR_PAINTING), PresContext());
michael@0 365 dirtyRect.MoveBy(-rootRect.TopLeft());
michael@0 366 }
michael@0 367
michael@0 368 // XXXbholley - I don't think huge images in SVGs are common enough to
michael@0 369 // warrant worrying about the responsiveness impact of doing synchronous
michael@0 370 // decodes. The extra code complexity of determinining when we want to
michael@0 371 // force sync probably just isn't worth it, so always pass FLAG_SYNC_DECODE
michael@0 372 uint32_t drawFlags = imgIContainer::FLAG_SYNC_DECODE;
michael@0 373
michael@0 374 if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) {
michael@0 375 // Package up the attributes of this image element which can override the
michael@0 376 // attributes of mImageContainer's internal SVG document.
michael@0 377 SVGImageContext context(imgElem->mPreserveAspectRatio.GetAnimValue());
michael@0 378
michael@0 379 nsRect destRect(0, 0,
michael@0 380 appUnitsPerDevPx * width,
michael@0 381 appUnitsPerDevPx * height);
michael@0 382
michael@0 383 // Note: Can't use DrawSingleUnscaledImage for the TYPE_VECTOR case.
michael@0 384 // That method needs our image to have a fixed native width & height,
michael@0 385 // and that's not always true for TYPE_VECTOR images.
michael@0 386 nsLayoutUtils::DrawSingleImage(
michael@0 387 aContext,
michael@0 388 mImageContainer,
michael@0 389 nsLayoutUtils::GetGraphicsFilterForFrame(this),
michael@0 390 destRect,
michael@0 391 aDirtyRect ? dirtyRect : destRect,
michael@0 392 &context,
michael@0 393 drawFlags);
michael@0 394 } else { // mImageContainer->GetType() == TYPE_RASTER
michael@0 395 nsLayoutUtils::DrawSingleUnscaledImage(
michael@0 396 aContext,
michael@0 397 mImageContainer,
michael@0 398 nsLayoutUtils::GetGraphicsFilterForFrame(this),
michael@0 399 nsPoint(0, 0),
michael@0 400 aDirtyRect ? &dirtyRect : nullptr,
michael@0 401 drawFlags);
michael@0 402 }
michael@0 403
michael@0 404 if (opacity != 1.0f || StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
michael@0 405 ctx->PopGroupToSource();
michael@0 406 ctx->SetOperator(gfxContext::OPERATOR_OVER);
michael@0 407 ctx->Paint(opacity);
michael@0 408 }
michael@0 409 // gfxContextAutoSaveRestore goes out of scope & cleans up our gfxContext
michael@0 410 }
michael@0 411
michael@0 412 return rv;
michael@0 413 }
michael@0 414
michael@0 415 nsIFrame*
michael@0 416 nsSVGImageFrame::GetFrameForPoint(const nsPoint &aPoint)
michael@0 417 {
michael@0 418 // Special case for raster images -- we only want to accept points that fall
michael@0 419 // in the underlying image's (transformed) native bounds. That region
michael@0 420 // doesn't necessarily map to our <image> element's [x,y,width,height]. So,
michael@0 421 // we have to look up the native image size & our image transform in order
michael@0 422 // to filter out points that fall outside that area.
michael@0 423 if (StyleDisplay()->IsScrollableOverflow() && mImageContainer) {
michael@0 424 if (mImageContainer->GetType() == imgIContainer::TYPE_RASTER) {
michael@0 425 int32_t nativeWidth, nativeHeight;
michael@0 426 if (NS_FAILED(mImageContainer->GetWidth(&nativeWidth)) ||
michael@0 427 NS_FAILED(mImageContainer->GetHeight(&nativeHeight)) ||
michael@0 428 nativeWidth == 0 || nativeHeight == 0) {
michael@0 429 return nullptr;
michael@0 430 }
michael@0 431
michael@0 432 if (!nsSVGUtils::HitTestRect(
michael@0 433 GetRasterImageTransform(nativeWidth, nativeHeight,
michael@0 434 FOR_HIT_TESTING),
michael@0 435 0, 0, nativeWidth, nativeHeight,
michael@0 436 PresContext()->AppUnitsToDevPixels(aPoint.x),
michael@0 437 PresContext()->AppUnitsToDevPixels(aPoint.y))) {
michael@0 438 return nullptr;
michael@0 439 }
michael@0 440 }
michael@0 441 // The special case above doesn't apply to vector images, because they
michael@0 442 // don't limit their drawing to explicit "native bounds" -- they have
michael@0 443 // an infinite canvas on which to place content. So it's reasonable to
michael@0 444 // just fall back on our <image> element's own bounds here.
michael@0 445 }
michael@0 446
michael@0 447 return nsSVGImageFrameBase::GetFrameForPoint(aPoint);
michael@0 448 }
michael@0 449
michael@0 450 nsIAtom *
michael@0 451 nsSVGImageFrame::GetType() const
michael@0 452 {
michael@0 453 return nsGkAtoms::svgImageFrame;
michael@0 454 }
michael@0 455
michael@0 456 //----------------------------------------------------------------------
michael@0 457 // nsSVGPathGeometryFrame methods:
michael@0 458
michael@0 459 // Lie about our fill/stroke so that covered region and hit detection work properly
michael@0 460
michael@0 461 void
michael@0 462 nsSVGImageFrame::ReflowSVG()
michael@0 463 {
michael@0 464 NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
michael@0 465 "This call is probably a wasteful mistake");
michael@0 466
michael@0 467 NS_ABORT_IF_FALSE(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
michael@0 468 "ReflowSVG mechanism not designed for this");
michael@0 469
michael@0 470 if (!nsSVGUtils::NeedsReflowSVG(this)) {
michael@0 471 return;
michael@0 472 }
michael@0 473
michael@0 474 float x, y, width, height;
michael@0 475 SVGImageElement *element = static_cast<SVGImageElement*>(mContent);
michael@0 476 element->GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
michael@0 477
michael@0 478 Rect extent(x, y, width, height);
michael@0 479
michael@0 480 if (!extent.IsEmpty()) {
michael@0 481 mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent,
michael@0 482 PresContext()->AppUnitsPerCSSPixel());
michael@0 483 } else {
michael@0 484 mRect.SetEmpty();
michael@0 485 }
michael@0 486
michael@0 487 if (mState & NS_FRAME_FIRST_REFLOW) {
michael@0 488 // Make sure we have our filter property (if any) before calling
michael@0 489 // FinishAndStoreOverflow (subsequent filter changes are handled off
michael@0 490 // nsChangeHint_UpdateEffects):
michael@0 491 nsSVGEffects::UpdateEffects(this);
michael@0 492
michael@0 493 if (!mReflowCallbackPosted) {
michael@0 494 nsIPresShell* shell = PresContext()->PresShell();
michael@0 495 mReflowCallbackPosted = true;
michael@0 496 shell->PostReflowCallback(this);
michael@0 497 }
michael@0 498 }
michael@0 499
michael@0 500 nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
michael@0 501 nsOverflowAreas overflowAreas(overflow, overflow);
michael@0 502 FinishAndStoreOverflow(overflowAreas, mRect.Size());
michael@0 503
michael@0 504 mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
michael@0 505 NS_FRAME_HAS_DIRTY_CHILDREN);
michael@0 506
michael@0 507 // Invalidate, but only if this is not our first reflow (since if it is our
michael@0 508 // first reflow then we haven't had our first paint yet).
michael@0 509 if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
michael@0 510 InvalidateFrame();
michael@0 511 }
michael@0 512 }
michael@0 513
michael@0 514 bool
michael@0 515 nsSVGImageFrame::ReflowFinished()
michael@0 516 {
michael@0 517 mReflowCallbackPosted = false;
michael@0 518
michael@0 519 nsLayoutUtils::UpdateImageVisibilityForFrame(this);
michael@0 520
michael@0 521 return false;
michael@0 522 }
michael@0 523
michael@0 524 void
michael@0 525 nsSVGImageFrame::ReflowCallbackCanceled()
michael@0 526 {
michael@0 527 mReflowCallbackPosted = false;
michael@0 528 }
michael@0 529
michael@0 530 uint16_t
michael@0 531 nsSVGImageFrame::GetHitTestFlags()
michael@0 532 {
michael@0 533 uint16_t flags = 0;
michael@0 534
michael@0 535 switch(StyleVisibility()->mPointerEvents) {
michael@0 536 case NS_STYLE_POINTER_EVENTS_NONE:
michael@0 537 break;
michael@0 538 case NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED:
michael@0 539 case NS_STYLE_POINTER_EVENTS_AUTO:
michael@0 540 if (StyleVisibility()->IsVisible()) {
michael@0 541 /* XXX: should check pixel transparency */
michael@0 542 flags |= SVG_HIT_TEST_FILL;
michael@0 543 }
michael@0 544 break;
michael@0 545 case NS_STYLE_POINTER_EVENTS_VISIBLEFILL:
michael@0 546 case NS_STYLE_POINTER_EVENTS_VISIBLESTROKE:
michael@0 547 case NS_STYLE_POINTER_EVENTS_VISIBLE:
michael@0 548 if (StyleVisibility()->IsVisible()) {
michael@0 549 flags |= SVG_HIT_TEST_FILL;
michael@0 550 }
michael@0 551 break;
michael@0 552 case NS_STYLE_POINTER_EVENTS_PAINTED:
michael@0 553 /* XXX: should check pixel transparency */
michael@0 554 flags |= SVG_HIT_TEST_FILL;
michael@0 555 break;
michael@0 556 case NS_STYLE_POINTER_EVENTS_FILL:
michael@0 557 case NS_STYLE_POINTER_EVENTS_STROKE:
michael@0 558 case NS_STYLE_POINTER_EVENTS_ALL:
michael@0 559 flags |= SVG_HIT_TEST_FILL;
michael@0 560 break;
michael@0 561 default:
michael@0 562 NS_ERROR("not reached");
michael@0 563 break;
michael@0 564 }
michael@0 565
michael@0 566 return flags;
michael@0 567 }
michael@0 568
michael@0 569 //----------------------------------------------------------------------
michael@0 570 // nsSVGImageListener implementation
michael@0 571
michael@0 572 NS_IMPL_ISUPPORTS(nsSVGImageListener, imgINotificationObserver)
michael@0 573
michael@0 574 nsSVGImageListener::nsSVGImageListener(nsSVGImageFrame *aFrame) : mFrame(aFrame)
michael@0 575 {
michael@0 576 }
michael@0 577
michael@0 578 NS_IMETHODIMP
michael@0 579 nsSVGImageListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
michael@0 580 {
michael@0 581 if (!mFrame)
michael@0 582 return NS_ERROR_FAILURE;
michael@0 583
michael@0 584 if (aType == imgINotificationObserver::LOAD_COMPLETE) {
michael@0 585 mFrame->InvalidateFrame();
michael@0 586 nsSVGEffects::InvalidateRenderingObservers(mFrame);
michael@0 587 nsSVGUtils::ScheduleReflowSVG(mFrame);
michael@0 588 }
michael@0 589
michael@0 590 if (aType == imgINotificationObserver::FRAME_UPDATE) {
michael@0 591 // No new dimensions, so we don't need to call
michael@0 592 // nsSVGUtils::InvalidateAndScheduleBoundsUpdate.
michael@0 593 nsSVGEffects::InvalidateRenderingObservers(mFrame);
michael@0 594 mFrame->InvalidateFrame();
michael@0 595 }
michael@0 596
michael@0 597 if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
michael@0 598 // Called once the resource's dimensions have been obtained.
michael@0 599 aRequest->GetImage(getter_AddRefs(mFrame->mImageContainer));
michael@0 600 mFrame->InvalidateFrame();
michael@0 601 nsSVGEffects::InvalidateRenderingObservers(mFrame);
michael@0 602 nsSVGUtils::ScheduleReflowSVG(mFrame);
michael@0 603 }
michael@0 604
michael@0 605 return NS_OK;
michael@0 606 }
michael@0 607

mercurial