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: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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 #include "gfxDrawable.h"
7 #include "gfxASurface.h"
8 #include "gfxContext.h"
9 #include "gfxImageSurface.h"
10 #include "gfxPlatform.h"
11 #include "gfxColor.h"
12 #ifdef MOZ_X11
13 #include "cairo.h"
14 #include "gfxXlibSurface.h"
15 #endif
17 using namespace mozilla;
18 using namespace mozilla::gfx;
20 gfxSurfaceDrawable::gfxSurfaceDrawable(gfxASurface* aSurface,
21 const gfxIntSize aSize,
22 const gfxMatrix aTransform)
23 : gfxDrawable(aSize)
24 , mSurface(aSurface)
25 , mTransform(aTransform)
26 {
27 }
29 gfxSurfaceDrawable::gfxSurfaceDrawable(DrawTarget* aDrawTarget,
30 const gfxIntSize aSize,
31 const gfxMatrix aTransform)
32 : gfxDrawable(aSize)
33 , mDrawTarget(aDrawTarget)
34 , mTransform(aTransform)
35 {
36 }
38 gfxSurfaceDrawable::gfxSurfaceDrawable(SourceSurface* aSurface,
39 const gfxIntSize aSize,
40 const gfxMatrix aTransform)
41 : gfxDrawable(aSize)
42 , mSourceSurface(aSurface)
43 , mTransform(aTransform)
44 {
45 }
47 static gfxMatrix
48 DeviceToImageTransform(gfxContext* aContext,
49 const gfxMatrix& aUserSpaceToImageSpace)
50 {
51 gfxFloat deviceX, deviceY;
52 nsRefPtr<gfxASurface> currentTarget =
53 aContext->CurrentSurface(&deviceX, &deviceY);
54 gfxMatrix currentMatrix = aContext->CurrentMatrix();
55 gfxMatrix deviceToUser = gfxMatrix(currentMatrix).Invert();
56 deviceToUser.Translate(-gfxPoint(-deviceX, -deviceY));
57 return gfxMatrix(deviceToUser).Multiply(aUserSpaceToImageSpace);
58 }
60 static void
61 PreparePatternForUntiledDrawing(gfxPattern* aPattern,
62 const gfxMatrix& aDeviceToImage,
63 gfxASurface *currentTarget,
64 const GraphicsFilter aDefaultFilter)
65 {
66 if (!currentTarget) {
67 // This happens if we're dealing with an Azure target.
68 aPattern->SetExtend(gfxPattern::EXTEND_PAD);
69 aPattern->SetFilter(aDefaultFilter);
70 return;
71 }
73 // In theory we can handle this using cairo's EXTEND_PAD,
74 // but implementation limitations mean we have to consult
75 // the surface type.
76 switch (currentTarget->GetType()) {
78 #ifdef MOZ_X11
79 case gfxSurfaceType::Xlib:
80 {
81 // See bugs 324698, 422179, and 468496. This is a workaround for
82 // XRender's RepeatPad not being implemented correctly on old X
83 // servers.
84 //
85 // In this situation, cairo avoids XRender and instead reads back
86 // to perform EXTEND_PAD with pixman. This is too slow so we
87 // avoid EXTEND_PAD and set the filter to CAIRO_FILTER_FAST ---
88 // otherwise, pixman's sampling will sample transparency for the
89 // outside edges and we'll get blurry edges.
90 //
91 // But don't do this for simple downscales because it's horrible.
92 // Downscaling means that device-space coordinates are
93 // scaled *up* to find the image pixel coordinates.
94 //
95 // Cairo, and hence Gecko, can use RepeatPad on Xorg 1.7. We
96 // enable EXTEND_PAD provided that we're running on a recent
97 // enough X server.
98 if (static_cast<gfxXlibSurface*>(currentTarget)->IsPadSlow()) {
99 bool isDownscale =
100 aDeviceToImage.xx >= 1.0 && aDeviceToImage.yy >= 1.0 &&
101 aDeviceToImage.xy == 0.0 && aDeviceToImage.yx == 0.0;
103 GraphicsFilter filter =
104 isDownscale ? aDefaultFilter : (const GraphicsFilter)GraphicsFilter::FILTER_FAST;
105 aPattern->SetFilter(filter);
107 // Use the default EXTEND_NONE
108 break;
109 }
110 // else fall through to EXTEND_PAD and the default filter.
111 }
112 #endif
114 default:
115 // turn on EXTEND_PAD.
116 // This is what we really want for all surface types, if the
117 // implementation was universally good.
118 aPattern->SetExtend(gfxPattern::EXTEND_PAD);
119 aPattern->SetFilter(aDefaultFilter);
120 break;
121 }
122 }
124 bool
125 gfxSurfaceDrawable::Draw(gfxContext* aContext,
126 const gfxRect& aFillRect,
127 bool aRepeat,
128 const GraphicsFilter& aFilter,
129 const gfxMatrix& aTransform)
130 {
131 nsRefPtr<gfxPattern> pattern;
132 if (mDrawTarget) {
133 if (aContext->IsCairo()) {
134 nsRefPtr<gfxASurface> source =
135 gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mDrawTarget);
136 pattern = new gfxPattern(source);
137 } else {
138 RefPtr<SourceSurface> source = mDrawTarget->Snapshot();
139 pattern = new gfxPattern(source, Matrix());
140 }
141 } else if (mSourceSurface) {
142 pattern = new gfxPattern(mSourceSurface, Matrix());
143 } else {
144 pattern = new gfxPattern(mSurface);
145 }
146 if (aRepeat) {
147 pattern->SetExtend(gfxPattern::EXTEND_REPEAT);
148 pattern->SetFilter(aFilter);
149 } else {
150 GraphicsFilter filter = aFilter;
151 if (aContext->CurrentMatrix().HasOnlyIntegerTranslation() &&
152 aTransform.HasOnlyIntegerTranslation())
153 {
154 // If we only have integer translation, no special filtering needs to
155 // happen and we explicitly use FILTER_FAST. This is fast for some
156 // backends.
157 filter = GraphicsFilter::FILTER_FAST;
158 }
159 nsRefPtr<gfxASurface> currentTarget = aContext->CurrentSurface();
160 gfxMatrix deviceSpaceToImageSpace =
161 DeviceToImageTransform(aContext, aTransform);
162 PreparePatternForUntiledDrawing(pattern, deviceSpaceToImageSpace,
163 currentTarget, filter);
164 }
165 pattern->SetMatrix(gfxMatrix(aTransform).Multiply(mTransform));
166 aContext->NewPath();
167 aContext->SetPattern(pattern);
168 aContext->Rectangle(aFillRect);
169 aContext->Fill();
170 // clear the pattern so that the snapshot is released before the
171 // drawable is destroyed
172 aContext->SetDeviceColor(gfxRGBA(0.0, 0.0, 0.0, 0.0));
173 return true;
174 }
176 already_AddRefed<gfxImageSurface>
177 gfxSurfaceDrawable::GetAsImageSurface()
178 {
179 if (mDrawTarget || mSourceSurface) {
180 // TODO: Find a way to implement this. The caller really wants a 'sub-image' of
181 // the original, without having to do a copy. GetDataSurface() might just copy,
182 // which isn't useful.
183 return nullptr;
185 }
186 return mSurface->GetAsImageSurface();
187 }
189 gfxCallbackDrawable::gfxCallbackDrawable(gfxDrawingCallback* aCallback,
190 const gfxIntSize aSize)
191 : gfxDrawable(aSize)
192 , mCallback(aCallback)
193 {
194 }
196 already_AddRefed<gfxSurfaceDrawable>
197 gfxCallbackDrawable::MakeSurfaceDrawable(const GraphicsFilter aFilter)
198 {
199 SurfaceFormat format =
200 gfxPlatform::GetPlatform()->Optimal2DFormatForContent(gfxContentType::COLOR_ALPHA);
201 RefPtr<DrawTarget> dt =
202 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(mSize.ToIntSize(),
203 format);
204 if (!dt)
205 return nullptr;
207 nsRefPtr<gfxContext> ctx = new gfxContext(dt);
208 Draw(ctx, gfxRect(0, 0, mSize.width, mSize.height), false, aFilter);
210 RefPtr<SourceSurface> surface = dt->Snapshot();
211 nsRefPtr<gfxSurfaceDrawable> drawable = new gfxSurfaceDrawable(surface, mSize);
212 return drawable.forget();
213 }
215 bool
216 gfxCallbackDrawable::Draw(gfxContext* aContext,
217 const gfxRect& aFillRect,
218 bool aRepeat,
219 const GraphicsFilter& aFilter,
220 const gfxMatrix& aTransform)
221 {
222 if (aRepeat && !mSurfaceDrawable) {
223 mSurfaceDrawable = MakeSurfaceDrawable(aFilter);
224 }
226 if (mSurfaceDrawable)
227 return mSurfaceDrawable->Draw(aContext, aFillRect, aRepeat, aFilter,
228 aTransform);
230 if (mCallback)
231 return (*mCallback)(aContext, aFillRect, aFilter, aTransform);
233 return false;
234 }
236 gfxPatternDrawable::gfxPatternDrawable(gfxPattern* aPattern,
237 const gfxIntSize aSize)
238 : gfxDrawable(aSize)
239 , mPattern(aPattern)
240 {
241 }
243 gfxPatternDrawable::~gfxPatternDrawable()
244 {
245 }
247 class DrawingCallbackFromDrawable : public gfxDrawingCallback {
248 public:
249 DrawingCallbackFromDrawable(gfxDrawable* aDrawable)
250 : mDrawable(aDrawable) {
251 NS_ASSERTION(aDrawable, "aDrawable is null!");
252 }
254 virtual ~DrawingCallbackFromDrawable() {}
256 virtual bool operator()(gfxContext* aContext,
257 const gfxRect& aFillRect,
258 const GraphicsFilter& aFilter,
259 const gfxMatrix& aTransform = gfxMatrix())
260 {
261 return mDrawable->Draw(aContext, aFillRect, false, aFilter,
262 aTransform);
263 }
264 private:
265 nsRefPtr<gfxDrawable> mDrawable;
266 };
268 already_AddRefed<gfxCallbackDrawable>
269 gfxPatternDrawable::MakeCallbackDrawable()
270 {
271 nsRefPtr<gfxDrawingCallback> callback =
272 new DrawingCallbackFromDrawable(this);
273 nsRefPtr<gfxCallbackDrawable> callbackDrawable =
274 new gfxCallbackDrawable(callback, mSize);
275 return callbackDrawable.forget();
276 }
278 bool
279 gfxPatternDrawable::Draw(gfxContext* aContext,
280 const gfxRect& aFillRect,
281 bool aRepeat,
282 const GraphicsFilter& aFilter,
283 const gfxMatrix& aTransform)
284 {
285 if (!mPattern)
286 return false;
288 if (aRepeat) {
289 // We can't use mPattern directly: We want our repeated tiles to have
290 // the size mSize, which might not be the case in mPattern.
291 // So we need to draw mPattern into a surface of size mSize, create
292 // a pattern from the surface and draw that pattern.
293 // gfxCallbackDrawable and gfxSurfaceDrawable already know how to do
294 // those things, so we use them here. Drawing mPattern into the surface
295 // will happen through this Draw() method with aRepeat = false.
296 nsRefPtr<gfxCallbackDrawable> callbackDrawable = MakeCallbackDrawable();
297 return callbackDrawable->Draw(aContext, aFillRect, true, aFilter,
298 aTransform);
299 }
301 aContext->NewPath();
302 gfxMatrix oldMatrix = mPattern->GetMatrix();
303 mPattern->SetMatrix(gfxMatrix(aTransform).Multiply(oldMatrix));
304 aContext->SetPattern(mPattern);
305 aContext->Rectangle(aFillRect);
306 aContext->Fill();
307 mPattern->SetMatrix(oldMatrix);
308 return true;
309 }