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 #define PANGO_ENABLE_BACKEND
7 #define PANGO_ENABLE_ENGINE
9 #include "gfxPlatformGtk.h"
10 #include "prenv.h"
12 #include "nsUnicharUtils.h"
13 #include "nsUnicodeProperties.h"
14 #include "gfx2DGlue.h"
15 #include "gfxFontconfigUtils.h"
16 #include "gfxPangoFonts.h"
17 #include "gfxContext.h"
18 #include "gfxUserFontSet.h"
19 #include "gfxFT2FontBase.h"
21 #include "mozilla/gfx/2D.h"
23 #include "cairo.h"
24 #include <gtk/gtk.h>
26 #include "gfxImageSurface.h"
27 #ifdef MOZ_X11
28 #include <gdk/gdkx.h>
29 #include "gfxXlibSurface.h"
30 #include "cairo-xlib.h"
31 #include "mozilla/Preferences.h"
33 /* Undefine the Status from Xlib since it will conflict with system headers on OSX */
34 #if defined(__APPLE__) && defined(Status)
35 #undef Status
36 #endif
38 #endif /* MOZ_X11 */
40 #include <fontconfig/fontconfig.h>
42 #include "nsMathUtils.h"
44 #define GDK_PIXMAP_SIZE_MAX 32767
46 using namespace mozilla;
47 using namespace mozilla::gfx;
48 using namespace mozilla::unicode;
50 gfxFontconfigUtils *gfxPlatformGtk::sFontconfigUtils = nullptr;
52 #if (MOZ_WIDGET_GTK == 2)
53 static cairo_user_data_key_t cairo_gdk_drawable_key;
54 #endif
56 #ifdef MOZ_X11
57 bool gfxPlatformGtk::sUseXRender = true;
58 #endif
60 gfxPlatformGtk::gfxPlatformGtk()
61 {
62 if (!sFontconfigUtils)
63 sFontconfigUtils = gfxFontconfigUtils::GetFontconfigUtils();
64 #ifdef MOZ_X11
65 sUseXRender = mozilla::Preferences::GetBool("gfx.xrender.enabled");
66 #endif
68 uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO) | BackendTypeBit(BackendType::SKIA);
69 uint32_t contentMask = BackendTypeBit(BackendType::CAIRO) | BackendTypeBit(BackendType::SKIA);
70 InitBackendPrefs(canvasMask, BackendType::CAIRO,
71 contentMask, BackendType::CAIRO);
72 }
74 gfxPlatformGtk::~gfxPlatformGtk()
75 {
76 gfxFontconfigUtils::Shutdown();
77 sFontconfigUtils = nullptr;
79 gfxPangoFontGroup::Shutdown();
80 }
82 already_AddRefed<gfxASurface>
83 gfxPlatformGtk::CreateOffscreenSurface(const IntSize& size,
84 gfxContentType contentType)
85 {
86 nsRefPtr<gfxASurface> newSurface;
87 bool needsClear = true;
88 gfxImageFormat imageFormat = OptimalFormatForContent(contentType);
89 #ifdef MOZ_X11
90 // XXX we really need a different interface here, something that passes
91 // in more context, including the display and/or target surface type that
92 // we should try to match
93 GdkScreen *gdkScreen = gdk_screen_get_default();
94 if (gdkScreen) {
95 if (UseXRender()) {
96 Screen *screen = gdk_x11_screen_get_xscreen(gdkScreen);
97 XRenderPictFormat* xrenderFormat =
98 gfxXlibSurface::FindRenderFormat(DisplayOfScreen(screen),
99 imageFormat);
101 if (xrenderFormat) {
102 newSurface = gfxXlibSurface::Create(screen, xrenderFormat,
103 ThebesIntSize(size));
104 }
105 } else {
106 // We're not going to use XRender, so we don't need to
107 // search for a render format
108 newSurface = new gfxImageSurface(ThebesIntSize(size), imageFormat);
109 // The gfxImageSurface ctor zeroes this for us, no need to
110 // waste time clearing again
111 needsClear = false;
112 }
113 }
114 #endif
116 if (!newSurface) {
117 // We couldn't create a native surface for whatever reason;
118 // e.g., no display, no RENDER, bad size, etc.
119 // Fall back to image surface for the data.
120 newSurface = new gfxImageSurface(ThebesIntSize(size), imageFormat);
121 }
123 if (newSurface->CairoStatus()) {
124 newSurface = nullptr; // surface isn't valid for some reason
125 }
127 if (newSurface && needsClear) {
128 gfxContext tmpCtx(newSurface);
129 tmpCtx.SetOperator(gfxContext::OPERATOR_CLEAR);
130 tmpCtx.Paint();
131 }
133 return newSurface.forget();
134 }
136 nsresult
137 gfxPlatformGtk::GetFontList(nsIAtom *aLangGroup,
138 const nsACString& aGenericFamily,
139 nsTArray<nsString>& aListOfFonts)
140 {
141 return sFontconfigUtils->GetFontList(aLangGroup, aGenericFamily,
142 aListOfFonts);
143 }
145 nsresult
146 gfxPlatformGtk::UpdateFontList()
147 {
148 return sFontconfigUtils->UpdateFontList();
149 }
151 nsresult
152 gfxPlatformGtk::ResolveFontName(const nsAString& aFontName,
153 FontResolverCallback aCallback,
154 void *aClosure,
155 bool& aAborted)
156 {
157 return sFontconfigUtils->ResolveFontName(aFontName, aCallback,
158 aClosure, aAborted);
159 }
161 nsresult
162 gfxPlatformGtk::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
163 {
164 return sFontconfigUtils->GetStandardFamilyName(aFontName, aFamilyName);
165 }
167 gfxFontGroup *
168 gfxPlatformGtk::CreateFontGroup(const nsAString &aFamilies,
169 const gfxFontStyle *aStyle,
170 gfxUserFontSet *aUserFontSet)
171 {
172 return new gfxPangoFontGroup(aFamilies, aStyle, aUserFontSet);
173 }
175 gfxFontEntry*
176 gfxPlatformGtk::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
177 const nsAString& aFontName)
178 {
179 return gfxPangoFontGroup::NewFontEntry(*aProxyEntry, aFontName);
180 }
182 gfxFontEntry*
183 gfxPlatformGtk::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
184 const uint8_t *aFontData, uint32_t aLength)
185 {
186 // passing ownership of the font data to the new font entry
187 return gfxPangoFontGroup::NewFontEntry(*aProxyEntry,
188 aFontData, aLength);
189 }
191 bool
192 gfxPlatformGtk::IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags)
193 {
194 // check for strange format flags
195 NS_ASSERTION(!(aFormatFlags & gfxUserFontSet::FLAG_FORMAT_NOT_USED),
196 "strange font format hint set");
198 // accept supported formats
199 // Pango doesn't apply features from AAT TrueType extensions.
200 // Assume that if this is the only SFNT format specified,
201 // then AAT extensions are required for complex script support.
202 if (aFormatFlags & (gfxUserFontSet::FLAG_FORMAT_WOFF |
203 gfxUserFontSet::FLAG_FORMAT_OPENTYPE |
204 gfxUserFontSet::FLAG_FORMAT_TRUETYPE)) {
205 return true;
206 }
208 // reject all other formats, known and unknown
209 if (aFormatFlags != 0) {
210 return false;
211 }
213 // no format hint set, need to look at data
214 return true;
215 }
217 static int32_t sDPI = 0;
219 int32_t
220 gfxPlatformGtk::GetDPI()
221 {
222 if (!sDPI) {
223 // Make sure init is run so we have a resolution
224 GdkScreen *screen = gdk_screen_get_default();
225 gtk_settings_get_for_screen(screen);
226 sDPI = int32_t(round(gdk_screen_get_resolution(screen)));
227 if (sDPI <= 0) {
228 // Fall back to something sane
229 sDPI = 96;
230 }
231 }
232 return sDPI;
233 }
235 gfxImageFormat
236 gfxPlatformGtk::GetOffscreenFormat()
237 {
238 // Make sure there is a screen
239 GdkScreen *screen = gdk_screen_get_default();
240 if (screen && gdk_visual_get_depth(gdk_visual_get_system()) == 16) {
241 return gfxImageFormat::RGB16_565;
242 }
244 return gfxImageFormat::RGB24;
245 }
247 static int sDepth = 0;
249 int
250 gfxPlatformGtk::GetScreenDepth() const
251 {
252 if (!sDepth) {
253 GdkScreen *screen = gdk_screen_get_default();
254 if (screen) {
255 sDepth = gdk_visual_get_depth(gdk_visual_get_system());
256 } else {
257 sDepth = 24;
258 }
260 }
262 return sDepth;
263 }
265 bool
266 gfxPlatformGtk::SupportsOffMainThreadCompositing()
267 {
268 // Nightly builds have OMTC support by default for Electrolysis testing.
269 #if defined(MOZ_X11) && !defined(NIGHTLY_BUILD)
270 return (PR_GetEnv("MOZ_USE_OMTC") != nullptr) ||
271 (PR_GetEnv("MOZ_OMTC_ENABLED") != nullptr);
272 #else
273 return true;
274 #endif
275 }
277 void
278 gfxPlatformGtk::GetPlatformCMSOutputProfile(void *&mem, size_t &size)
279 {
280 mem = nullptr;
281 size = 0;
283 #ifdef MOZ_X11
284 const char EDID1_ATOM_NAME[] = "XFree86_DDC_EDID1_RAWDATA";
285 const char ICC_PROFILE_ATOM_NAME[] = "_ICC_PROFILE";
287 Atom edidAtom, iccAtom;
288 Display *dpy = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
289 // In xpcshell tests, we never initialize X and hence don't have a Display.
290 // In this case, there's no output colour management to be done, so we just
291 // return with nullptr.
292 if (!dpy)
293 return;
295 Window root = gdk_x11_get_default_root_xwindow();
297 Atom retAtom;
298 int retFormat;
299 unsigned long retLength, retAfter;
300 unsigned char *retProperty ;
302 iccAtom = XInternAtom(dpy, ICC_PROFILE_ATOM_NAME, TRUE);
303 if (iccAtom) {
304 // read once to get size, once for the data
305 if (Success == XGetWindowProperty(dpy, root, iccAtom,
306 0, INT_MAX /* length */,
307 False, AnyPropertyType,
308 &retAtom, &retFormat, &retLength,
309 &retAfter, &retProperty)) {
311 if (retLength > 0) {
312 void *buffer = malloc(retLength);
313 if (buffer) {
314 memcpy(buffer, retProperty, retLength);
315 mem = buffer;
316 size = retLength;
317 }
318 }
320 XFree(retProperty);
321 if (size > 0) {
322 #ifdef DEBUG_tor
323 fprintf(stderr,
324 "ICM profile read from %s successfully\n",
325 ICC_PROFILE_ATOM_NAME);
326 #endif
327 return;
328 }
329 }
330 }
332 edidAtom = XInternAtom(dpy, EDID1_ATOM_NAME, TRUE);
333 if (edidAtom) {
334 if (Success == XGetWindowProperty(dpy, root, edidAtom, 0, 32,
335 False, AnyPropertyType,
336 &retAtom, &retFormat, &retLength,
337 &retAfter, &retProperty)) {
338 double gamma;
339 qcms_CIE_xyY whitePoint;
340 qcms_CIE_xyYTRIPLE primaries;
342 if (retLength != 128) {
343 #ifdef DEBUG_tor
344 fprintf(stderr, "Short EDID data\n");
345 #endif
346 return;
347 }
349 // Format documented in "VESA E-EDID Implementation Guide"
351 gamma = (100 + retProperty[0x17]) / 100.0;
352 whitePoint.x = ((retProperty[0x21] << 2) |
353 (retProperty[0x1a] >> 2 & 3)) / 1024.0;
354 whitePoint.y = ((retProperty[0x22] << 2) |
355 (retProperty[0x1a] >> 0 & 3)) / 1024.0;
356 whitePoint.Y = 1.0;
358 primaries.red.x = ((retProperty[0x1b] << 2) |
359 (retProperty[0x19] >> 6 & 3)) / 1024.0;
360 primaries.red.y = ((retProperty[0x1c] << 2) |
361 (retProperty[0x19] >> 4 & 3)) / 1024.0;
362 primaries.red.Y = 1.0;
364 primaries.green.x = ((retProperty[0x1d] << 2) |
365 (retProperty[0x19] >> 2 & 3)) / 1024.0;
366 primaries.green.y = ((retProperty[0x1e] << 2) |
367 (retProperty[0x19] >> 0 & 3)) / 1024.0;
368 primaries.green.Y = 1.0;
370 primaries.blue.x = ((retProperty[0x1f] << 2) |
371 (retProperty[0x1a] >> 6 & 3)) / 1024.0;
372 primaries.blue.y = ((retProperty[0x20] << 2) |
373 (retProperty[0x1a] >> 4 & 3)) / 1024.0;
374 primaries.blue.Y = 1.0;
376 XFree(retProperty);
378 #ifdef DEBUG_tor
379 fprintf(stderr, "EDID gamma: %f\n", gamma);
380 fprintf(stderr, "EDID whitepoint: %f %f %f\n",
381 whitePoint.x, whitePoint.y, whitePoint.Y);
382 fprintf(stderr, "EDID primaries: [%f %f %f] [%f %f %f] [%f %f %f]\n",
383 primaries.Red.x, primaries.Red.y, primaries.Red.Y,
384 primaries.Green.x, primaries.Green.y, primaries.Green.Y,
385 primaries.Blue.x, primaries.Blue.y, primaries.Blue.Y);
386 #endif
388 qcms_data_create_rgb_with_gamma(whitePoint, primaries, gamma, &mem, &size);
390 #ifdef DEBUG_tor
391 if (size > 0) {
392 fprintf(stderr,
393 "ICM profile read from %s successfully\n",
394 EDID1_ATOM_NAME);
395 }
396 #endif
397 }
398 }
399 #endif
400 }
403 #if (MOZ_WIDGET_GTK == 2)
404 void
405 gfxPlatformGtk::SetGdkDrawable(cairo_surface_t *target,
406 GdkDrawable *drawable)
407 {
408 if (cairo_surface_status(target))
409 return;
411 g_object_ref(drawable);
413 cairo_surface_set_user_data (target,
414 &cairo_gdk_drawable_key,
415 drawable,
416 g_object_unref);
417 }
419 GdkDrawable *
420 gfxPlatformGtk::GetGdkDrawable(cairo_surface_t *target)
421 {
422 if (cairo_surface_status(target))
423 return nullptr;
425 GdkDrawable *result;
427 result = (GdkDrawable*) cairo_surface_get_user_data (target,
428 &cairo_gdk_drawable_key);
429 if (result)
430 return result;
432 #ifdef MOZ_X11
433 if (cairo_surface_get_type(target) != CAIRO_SURFACE_TYPE_XLIB)
434 return nullptr;
436 // try looking it up in gdk's table
437 result = (GdkDrawable*) gdk_xid_table_lookup(cairo_xlib_surface_get_drawable(target));
438 if (result) {
439 SetGdkDrawable(target, result);
440 return result;
441 }
442 #endif
444 return nullptr;
445 }
446 #endif
448 TemporaryRef<ScaledFont>
449 gfxPlatformGtk::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont)
450 {
451 return GetScaledFontForFontWithCairoSkia(aTarget, aFont);
452 }