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