gfx/thebes/gfxXlibSurface.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.

     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 "gfxXlibSurface.h"
     8 #include "cairo.h"
     9 #include "cairo-xlib.h"
    10 #include "cairo-xlib-xrender.h"
    11 #include <X11/Xlibint.h>	/* For XESetCloseDisplay */
    12 #undef max // Xlibint.h defines this and it breaks std::max
    13 #undef min // Xlibint.h defines this and it breaks std::min
    15 #include "nsAutoPtr.h"
    16 #include "nsTArray.h"
    17 #include "nsAlgorithm.h"
    18 #include "mozilla/Preferences.h"
    19 #include <algorithm>
    20 #include "mozilla/CheckedInt.h"
    22 using namespace mozilla;
    24 // Although the dimension parameters in the xCreatePixmapReq wire protocol are
    25 // 16-bit unsigned integers, the server's CreatePixmap returns BadAlloc if
    26 // either dimension cannot be represented by a 16-bit *signed* integer.
    27 #define XLIB_IMAGE_SIDE_SIZE_LIMIT 0x7fff
    29 gfxXlibSurface::gfxXlibSurface(Display *dpy, Drawable drawable, Visual *visual)
    30     : mPixmapTaken(false), mDisplay(dpy), mDrawable(drawable)
    31 #if defined(GL_PROVIDER_GLX)
    32     , mGLXPixmap(None)
    33 #endif
    34 {
    35     const gfxIntSize size = DoSizeQuery();
    36     cairo_surface_t *surf = cairo_xlib_surface_create(dpy, drawable, visual, size.width, size.height);
    37     Init(surf);
    38 }
    40 gfxXlibSurface::gfxXlibSurface(Display *dpy, Drawable drawable, Visual *visual, const gfxIntSize& size)
    41     : mPixmapTaken(false), mDisplay(dpy), mDrawable(drawable)
    42 #if defined(GL_PROVIDER_GLX)
    43     , mGLXPixmap(None)
    44 #endif
    45 {
    46     NS_ASSERTION(CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT),
    47                  "Bad size");
    49     cairo_surface_t *surf = cairo_xlib_surface_create(dpy, drawable, visual, size.width, size.height);
    50     Init(surf);
    51 }
    53 gfxXlibSurface::gfxXlibSurface(Screen *screen, Drawable drawable, XRenderPictFormat *format,
    54                                const gfxIntSize& size)
    55     : mPixmapTaken(false), mDisplay(DisplayOfScreen(screen)),
    56       mDrawable(drawable)
    57 #if defined(GL_PROVIDER_GLX)
    58       , mGLXPixmap(None)
    59 #endif
    60 {
    61     NS_ASSERTION(CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT),
    62                  "Bad Size");
    64     cairo_surface_t *surf =
    65         cairo_xlib_surface_create_with_xrender_format(mDisplay, drawable,
    66                                                       screen, format,
    67                                                       size.width, size.height);
    68     Init(surf);
    69 }
    71 gfxXlibSurface::gfxXlibSurface(cairo_surface_t *csurf)
    72     : mPixmapTaken(false)
    73 #if defined(GL_PROVIDER_GLX)
    74       , mGLXPixmap(None)
    75 #endif
    76 {
    77     NS_PRECONDITION(cairo_surface_status(csurf) == 0,
    78                     "Not expecting an error surface");
    80     mDrawable = cairo_xlib_surface_get_drawable(csurf);
    81     mDisplay = cairo_xlib_surface_get_display(csurf);
    83     Init(csurf, true);
    84 }
    86 gfxXlibSurface::~gfxXlibSurface()
    87 {
    88 #if defined(GL_PROVIDER_GLX)
    89     if (mGLXPixmap) {
    90         gl::sGLXLibrary.DestroyPixmap(mDisplay, mGLXPixmap);
    91     }
    92 #endif
    93     // gfxASurface's destructor calls RecordMemoryFreed().
    94     if (mPixmapTaken) {
    95         XFreePixmap (mDisplay, mDrawable);
    96     }
    97 }
    99 static Drawable
   100 CreatePixmap(Screen *screen, const gfxIntSize& size, unsigned int depth,
   101              Drawable relatedDrawable)
   102 {
   103     if (!gfxASurface::CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT))
   104         return None;
   106     if (relatedDrawable == None) {
   107         relatedDrawable = RootWindowOfScreen(screen);
   108     }
   109     Display *dpy = DisplayOfScreen(screen);
   110     // X gives us a fatal error if we try to create a pixmap of width
   111     // or height 0
   112     return XCreatePixmap(dpy, relatedDrawable,
   113                          std::max(1, size.width), std::max(1, size.height),
   114                          depth);
   115 }
   117 void
   118 gfxXlibSurface::TakePixmap()
   119 {
   120     NS_ASSERTION(!mPixmapTaken, "I already own the Pixmap!");
   121     mPixmapTaken = true;
   123     // The bit depth returned from Cairo is technically int, but this is
   124     // the last place we'd be worried about that scenario.
   125     unsigned int bitDepth = cairo_xlib_surface_get_depth(CairoSurface());
   126     MOZ_ASSERT((bitDepth % 8) == 0, "Memory used not recorded correctly");    
   128     // Divide by 8 because surface_get_depth gives us the number of *bits* per
   129     // pixel.
   130     gfxIntSize size = GetSize();
   131     CheckedInt32 totalBytes = CheckedInt32(size.width) * CheckedInt32(size.height) * (bitDepth/8);
   133     // Don't do anything in the "else" case.  We could add INT32_MAX, but that
   134     // would overflow the memory used counter.  It would also mean we tried for
   135     // a 2G image.  For now, we'll just assert,
   136     MOZ_ASSERT(totalBytes.isValid(),"Did not expect to exceed 2Gb image");
   137     if (totalBytes.isValid()) {
   138         RecordMemoryUsed(totalBytes.value());
   139     }
   140 }
   142 Drawable
   143 gfxXlibSurface::ReleasePixmap() {
   144     NS_ASSERTION(mPixmapTaken, "I don't own the Pixmap!");
   145     mPixmapTaken = false;
   146     RecordMemoryFreed();
   147     return mDrawable;
   148 }
   150 static cairo_user_data_key_t gDestroyPixmapKey;
   152 struct DestroyPixmapClosure {
   153     DestroyPixmapClosure(Drawable d, Screen *s) : mPixmap(d), mScreen(s) {}
   154     Drawable mPixmap;
   155     Screen *mScreen;
   156 };
   158 static void
   159 DestroyPixmap(void *data)
   160 {
   161     DestroyPixmapClosure *closure = static_cast<DestroyPixmapClosure*>(data);
   162     XFreePixmap(DisplayOfScreen(closure->mScreen), closure->mPixmap);
   163     delete closure;
   164 }
   166 /* static */
   167 cairo_surface_t *
   168 gfxXlibSurface::CreateCairoSurface(Screen *screen, Visual *visual,
   169                                    const gfxIntSize& size, Drawable relatedDrawable)
   170 {
   171     Drawable drawable =
   172         CreatePixmap(screen, size, DepthOfVisual(screen, visual),
   173                      relatedDrawable);
   174     if (!drawable)
   175         return nullptr;
   177     cairo_surface_t* surface =
   178         cairo_xlib_surface_create(DisplayOfScreen(screen), drawable, visual,
   179                                   size.width, size.height);
   180     if (cairo_surface_status(surface)) {
   181         cairo_surface_destroy(surface);
   182         XFreePixmap(DisplayOfScreen(screen), drawable);
   183         return nullptr;
   184     }
   186     DestroyPixmapClosure *closure = new DestroyPixmapClosure(drawable, screen);
   187     cairo_surface_set_user_data(surface, &gDestroyPixmapKey,
   188                                 closure, DestroyPixmap);
   189     return surface;
   190 }
   192 /* static */
   193 already_AddRefed<gfxXlibSurface>
   194 gfxXlibSurface::Create(Screen *screen, Visual *visual,
   195                        const gfxIntSize& size, Drawable relatedDrawable)
   196 {
   197     Drawable drawable =
   198         CreatePixmap(screen, size, DepthOfVisual(screen, visual),
   199                      relatedDrawable);
   200     if (!drawable)
   201         return nullptr;
   203     nsRefPtr<gfxXlibSurface> result =
   204         new gfxXlibSurface(DisplayOfScreen(screen), drawable, visual, size);
   205     result->TakePixmap();
   207     if (result->CairoStatus() != 0)
   208         return nullptr;
   210     return result.forget();
   211 }
   213 /* static */
   214 already_AddRefed<gfxXlibSurface>
   215 gfxXlibSurface::Create(Screen *screen, XRenderPictFormat *format,
   216                        const gfxIntSize& size, Drawable relatedDrawable)
   217 {
   218     Drawable drawable =
   219         CreatePixmap(screen, size, format->depth, relatedDrawable);
   220     if (!drawable)
   221         return nullptr;
   223     nsRefPtr<gfxXlibSurface> result =
   224         new gfxXlibSurface(screen, drawable, format, size);
   225     result->TakePixmap();
   227     if (result->CairoStatus() != 0)
   228         return nullptr;
   230     return result.forget();
   231 }
   233 static bool GetForce24bppPref()
   234 {
   235     return Preferences::GetBool("mozilla.widget.force-24bpp", false);
   236 }
   238 already_AddRefed<gfxASurface>
   239 gfxXlibSurface::CreateSimilarSurface(gfxContentType aContent,
   240                                      const gfxIntSize& aSize)
   241 {
   242     if (!mSurface || !mSurfaceValid) {
   243       return nullptr;
   244     }
   246     if (aContent == gfxContentType::COLOR) {
   247         // cairo_surface_create_similar will use a matching visual if it can.
   248         // However, systems with 16-bit or indexed default visuals may benefit
   249         // from rendering with 24-bit formats.
   250         static bool force24bpp = GetForce24bppPref();
   251         if (force24bpp
   252             && cairo_xlib_surface_get_depth(CairoSurface()) != 24) {
   253             XRenderPictFormat* format =
   254                 XRenderFindStandardFormat(mDisplay, PictStandardRGB24);
   255             if (format) {
   256                 // Cairo only performs simple self-copies as desired if it
   257                 // knows that this is a Pixmap surface.  It only knows that
   258                 // surfaces are pixmap surfaces if it creates the Pixmap
   259                 // itself, so we use cairo_surface_create_similar with a
   260                 // temporary reference surface to indicate the format.
   261                 Screen* screen = cairo_xlib_surface_get_screen(CairoSurface());
   262                 nsRefPtr<gfxXlibSurface> depth24reference =
   263                     gfxXlibSurface::Create(screen, format,
   264                                            gfxIntSize(1, 1), mDrawable);
   265                 if (depth24reference)
   266                     return depth24reference->
   267                         gfxASurface::CreateSimilarSurface(aContent, aSize);
   268             }
   269         }
   270     }
   272     return gfxASurface::CreateSimilarSurface(aContent, aSize);
   273 }
   275 void
   276 gfxXlibSurface::Finish()
   277 {
   278 #if defined(GL_PROVIDER_GLX)
   279     if (mGLXPixmap) {
   280         gl::sGLXLibrary.DestroyPixmap(mDisplay, mGLXPixmap);
   281         mGLXPixmap = None;
   282     }
   283 #endif
   284     gfxASurface::Finish();
   285 }
   287 const gfxIntSize
   288 gfxXlibSurface::GetSize() const
   289 {
   290     if (!mSurfaceValid)
   291         return gfxIntSize(0,0);
   293     return gfxIntSize(cairo_xlib_surface_get_width(mSurface),
   294                       cairo_xlib_surface_get_height(mSurface));
   295 }
   297 const gfxIntSize
   298 gfxXlibSurface::DoSizeQuery()
   299 {
   300     // figure out width/height/depth
   301     Window root_ignore;
   302     int x_ignore, y_ignore;
   303     unsigned int bwidth_ignore, width, height, depth;
   305     XGetGeometry(mDisplay,
   306                  mDrawable,
   307                  &root_ignore, &x_ignore, &y_ignore,
   308                  &width, &height,
   309                  &bwidth_ignore, &depth);
   311     return gfxIntSize(width, height);
   312 }
   314 class DisplayTable {
   315 public:
   316     static bool GetColormapAndVisual(Screen* screen,
   317                                        XRenderPictFormat* format,
   318                                        Visual* visual, Colormap* colormap,
   319                                        Visual** visualForColormap);
   321 private:
   322     struct ColormapEntry {
   323         XRenderPictFormat* mFormat;
   324         // The Screen is needed here because colormaps (and their visuals) may
   325         // only be used on one Screen, but XRenderPictFormats are not unique
   326         // to any one Screen.
   327         Screen* mScreen;
   328         Visual* mVisual;
   329         Colormap mColormap;
   330     };
   332     class DisplayInfo {
   333     public:
   334         DisplayInfo(Display* display) : mDisplay(display) { }
   335         Display* mDisplay;
   336         nsTArray<ColormapEntry> mColormapEntries;
   337     };
   339     // Comparator for finding the DisplayInfo
   340     class FindDisplay {
   341     public:
   342         bool Equals(const DisplayInfo& info, const Display *display) const
   343         {
   344             return info.mDisplay == display;
   345         }
   346     };
   348     static int DisplayClosing(Display *display, XExtCodes* codes);
   350     nsTArray<DisplayInfo> mDisplays;
   351     static DisplayTable* sDisplayTable;
   352 };
   354 DisplayTable* DisplayTable::sDisplayTable;
   356 // Pixmaps don't have a particular associated visual but the pixel values are
   357 // interpreted according to a visual/colormap pairs.
   358 //
   359 // cairo is designed for surfaces with either TrueColor visuals or the
   360 // default visual (which may not be true color).  TrueColor visuals don't
   361 // really need a colormap because the visual indicates the pixel format,
   362 // and cairo uses the default visual with the default colormap, so cairo
   363 // surfaces don't need an explicit colormap.
   364 //
   365 // However, some toolkits (e.g. GDK) need a colormap even with TrueColor
   366 // visuals.  We can create a colormap for these visuals, but it will use about
   367 // 20kB of memory in the server, so we use the default colormap when
   368 // suitable and share colormaps between surfaces.  Another reason for
   369 // minimizing colormap turnover is that the plugin process must leak resources
   370 // for each new colormap id when using older GDK libraries (bug 569775).
   371 //
   372 // Only the format of the pixels is important for rendering to Pixmaps, so if
   373 // the format of a visual matches that of the surface, then that visual can be
   374 // used for rendering to the surface.  Multiple visuals can match the same
   375 // format (but have different GLX properties), so the visual returned may
   376 // differ from the visual passed in.  Colormaps are tied to a visual, so
   377 // should only be used with their visual.
   379 /* static */ bool
   380 DisplayTable::GetColormapAndVisual(Screen* aScreen, XRenderPictFormat* aFormat,
   381                                    Visual* aVisual, Colormap* aColormap,
   382                                    Visual** aVisualForColormap)
   384 {
   385     Display* display = DisplayOfScreen(aScreen);
   387     // Use the default colormap if the default visual matches.
   388     Visual *defaultVisual = DefaultVisualOfScreen(aScreen);
   389     if (aVisual == defaultVisual
   390         || (aFormat
   391             && aFormat == XRenderFindVisualFormat(display, defaultVisual)))
   392     {
   393         *aColormap = DefaultColormapOfScreen(aScreen);
   394         *aVisualForColormap = defaultVisual;
   395         return true;
   396     }
   398     // Only supporting TrueColor non-default visuals
   399     if (!aVisual || aVisual->c_class != TrueColor)
   400         return false;
   402     if (!sDisplayTable) {
   403         sDisplayTable = new DisplayTable();
   404     }
   406     nsTArray<DisplayInfo>* displays = &sDisplayTable->mDisplays;
   407     uint32_t d = displays->IndexOf(display, 0, FindDisplay());
   409     if (d == displays->NoIndex) {
   410         d = displays->Length();
   411         // Register for notification of display closing, when this info
   412         // becomes invalid.
   413         XExtCodes *codes = XAddExtension(display);
   414         if (!codes)
   415             return false;
   417         XESetCloseDisplay(display, codes->extension, DisplayClosing);
   418         // Add a new DisplayInfo.
   419         displays->AppendElement(display);
   420     }
   422     nsTArray<ColormapEntry>* entries =
   423         &displays->ElementAt(d).mColormapEntries;
   425     // Only a small number of formats are expected to be used, so just do a
   426     // simple linear search.
   427     for (uint32_t i = 0; i < entries->Length(); ++i) {
   428         const ColormapEntry& entry = entries->ElementAt(i);
   429         // Only the format and screen need to match.  (The visual may differ.)
   430         // If there is no format (e.g. no RENDER extension) then just compare
   431         // the visual.
   432         if ((aFormat && entry.mFormat == aFormat && entry.mScreen == aScreen)
   433             || aVisual == entry.mVisual) {
   434             *aColormap = entry.mColormap;
   435             *aVisualForColormap = entry.mVisual;
   436             return true;
   437         }
   438     }
   440     // No existing entry.  Create a colormap and add an entry.
   441     Colormap colormap = XCreateColormap(display, RootWindowOfScreen(aScreen),
   442                                         aVisual, AllocNone);
   443     ColormapEntry* newEntry = entries->AppendElement();
   444     newEntry->mFormat = aFormat;
   445     newEntry->mScreen = aScreen;
   446     newEntry->mVisual = aVisual;
   447     newEntry->mColormap = colormap;
   449     *aColormap = colormap;
   450     *aVisualForColormap = aVisual;
   451     return true;
   452 }
   454 /* static */ int
   455 DisplayTable::DisplayClosing(Display *display, XExtCodes* codes)
   456 {
   457     // No need to free the colormaps explicitly as they will be released when
   458     // the connection is closed.
   459     sDisplayTable->mDisplays.RemoveElement(display, FindDisplay());
   460     if (sDisplayTable->mDisplays.Length() == 0) {
   461         delete sDisplayTable;
   462         sDisplayTable = nullptr;
   463     }
   464     return 0;
   465 }
   467 /* static */
   468 bool
   469 gfxXlibSurface::GetColormapAndVisual(cairo_surface_t* aXlibSurface,
   470                                      Colormap* aColormap, Visual** aVisual)
   471 {
   472     XRenderPictFormat* format =
   473         cairo_xlib_surface_get_xrender_format(aXlibSurface);
   474     Screen* screen = cairo_xlib_surface_get_screen(aXlibSurface);
   475     Visual* visual = cairo_xlib_surface_get_visual(aXlibSurface);
   477     return DisplayTable::GetColormapAndVisual(screen, format, visual,
   478                                               aColormap, aVisual);
   479 }
   481 bool
   482 gfxXlibSurface::GetColormapAndVisual(Colormap* aColormap, Visual** aVisual)
   483 {
   484     if (!mSurfaceValid)
   485         return false;
   487     return GetColormapAndVisual(CairoSurface(), aColormap, aVisual);
   488 }
   490 /* static */
   491 int
   492 gfxXlibSurface::DepthOfVisual(const Screen* screen, const Visual* visual)
   493 {
   494     for (int d = 0; d < screen->ndepths; d++) {
   495         const Depth& d_info = screen->depths[d];
   496         if (visual >= &d_info.visuals[0]
   497             && visual < &d_info.visuals[d_info.nvisuals])
   498             return d_info.depth;
   499     }
   501     NS_ERROR("Visual not on Screen.");
   502     return 0;
   503 }
   505 /* static */
   506 Visual*
   507 gfxXlibSurface::FindVisual(Screen *screen, gfxImageFormat format)
   508 {
   509     int depth;
   510     unsigned long red_mask, green_mask, blue_mask;
   511     switch (format) {
   512         case gfxImageFormat::ARGB32:
   513             depth = 32;
   514             red_mask = 0xff0000;
   515             green_mask = 0xff00;
   516             blue_mask = 0xff;
   517             break;
   518         case gfxImageFormat::RGB24:
   519             depth = 24;
   520             red_mask = 0xff0000;
   521             green_mask = 0xff00;
   522             blue_mask = 0xff;
   523             break;
   524         case gfxImageFormat::RGB16_565:
   525             depth = 16;
   526             red_mask = 0xf800;
   527             green_mask = 0x7e0;
   528             blue_mask = 0x1f;
   529             break;
   530         case gfxImageFormat::A8:
   531         case gfxImageFormat::A1:
   532         default:
   533             return nullptr;
   534     }
   536     for (int d = 0; d < screen->ndepths; d++) {
   537         const Depth& d_info = screen->depths[d];
   538         if (d_info.depth != depth)
   539             continue;
   541         for (int v = 0; v < d_info.nvisuals; v++) {
   542             Visual* visual = &d_info.visuals[v];
   544             if (visual->c_class == TrueColor &&
   545                 visual->red_mask == red_mask &&
   546                 visual->green_mask == green_mask &&
   547                 visual->blue_mask == blue_mask)
   548                 return visual;
   549         }
   550     }
   552     return nullptr;
   553 }
   555 /* static */
   556 XRenderPictFormat*
   557 gfxXlibSurface::FindRenderFormat(Display *dpy, gfxImageFormat format)
   558 {
   559     switch (format) {
   560         case gfxImageFormat::ARGB32:
   561             return XRenderFindStandardFormat (dpy, PictStandardARGB32);
   562         case gfxImageFormat::RGB24:
   563             return XRenderFindStandardFormat (dpy, PictStandardRGB24);
   564         case gfxImageFormat::RGB16_565: {
   565             // PictStandardRGB16_565 is not standard Xrender format
   566             // we should try to find related visual
   567             // and find xrender format by visual
   568             Visual *visual = FindVisual(DefaultScreenOfDisplay(dpy), format);
   569             if (!visual)
   570                 return nullptr;
   571             return XRenderFindVisualFormat(dpy, visual);
   572         }
   573         case gfxImageFormat::A8:
   574             return XRenderFindStandardFormat (dpy, PictStandardA8);
   575         case gfxImageFormat::A1:
   576             return XRenderFindStandardFormat (dpy, PictStandardA1);
   577         default:
   578             break;
   579     }
   581     return nullptr;
   582 }
   584 Screen*
   585 gfxXlibSurface::XScreen()
   586 {
   587     return cairo_xlib_surface_get_screen(CairoSurface());
   588 }
   590 XRenderPictFormat*
   591 gfxXlibSurface::XRenderFormat()
   592 {
   593     return cairo_xlib_surface_get_xrender_format(CairoSurface());
   594 }
   596 #if defined(GL_PROVIDER_GLX)
   597 GLXPixmap
   598 gfxXlibSurface::GetGLXPixmap()
   599 {
   600     if (!mGLXPixmap) {
   601 #ifdef DEBUG
   602         // cairo_surface_has_show_text_glyphs is used solely for the
   603         // side-effect of setting the error on surface if
   604         // cairo_surface_finish() has been called.
   605         cairo_surface_has_show_text_glyphs(CairoSurface());
   606         NS_ASSERTION(CairoStatus() != CAIRO_STATUS_SURFACE_FINISHED,
   607             "GetGLXPixmap called after surface finished");
   608 #endif
   609         mGLXPixmap = gl::sGLXLibrary.CreatePixmap(this);
   610     }
   611     return mGLXPixmap;
   612 }
   613 #endif
   615 gfxMemoryLocation
   616 gfxXlibSurface::GetMemoryLocation() const
   617 {
   618     return gfxMemoryLocation::OUT_OF_PROCESS;
   619 }

mercurial