widget/gtk/nsScreenManagerGtk.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
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 #include "nsScreenManagerGtk.h"
     7 #include "nsScreenGtk.h"
     8 #include "nsIComponentManager.h"
     9 #include "nsRect.h"
    10 #include "nsAutoPtr.h"
    12 #define SCREEN_MANAGER_LIBRARY_LOAD_FAILED ((PRLibrary*)1)
    14 #ifdef MOZ_X11
    15 #include <gdk/gdkx.h>
    16 // prototypes from Xinerama.h
    17 typedef Bool (*_XnrmIsActive_fn)(Display *dpy);
    18 typedef XineramaScreenInfo* (*_XnrmQueryScreens_fn)(Display *dpy, int *number);
    19 #endif
    21 #include <gtk/gtk.h>
    24 static GdkFilterReturn
    25 root_window_event_filter(GdkXEvent *aGdkXEvent, GdkEvent *aGdkEvent,
    26                          gpointer aClosure)
    27 {
    28   nsScreenManagerGtk *manager = static_cast<nsScreenManagerGtk*>(aClosure);
    29 #ifdef MOZ_X11
    30   XEvent *xevent = static_cast<XEvent*>(aGdkXEvent);
    32   // See comments in nsScreenGtk::Init below.
    33   switch (xevent->type) {
    34     case ConfigureNotify:
    35       manager->Init();
    36       break;
    37     case PropertyNotify:
    38       {
    39         XPropertyEvent *propertyEvent = &xevent->xproperty;
    40         if (propertyEvent->atom == manager->NetWorkareaAtom()) {
    41           manager->Init();
    42         }
    43       }
    44       break;
    45     default:
    46       break;
    47   }
    48 #endif
    50   return GDK_FILTER_CONTINUE;
    51 }
    53 nsScreenManagerGtk :: nsScreenManagerGtk ( )
    54   : mXineramalib(nullptr)
    55   , mRootWindow(nullptr)
    56 {
    57   // nothing else to do. I guess we could cache a bunch of information
    58   // here, but we want to ask the device at runtime in case anything
    59   // has changed.
    60 }
    63 nsScreenManagerGtk :: ~nsScreenManagerGtk()
    64 {
    65   if (mRootWindow) {
    66     gdk_window_remove_filter(mRootWindow, root_window_event_filter, this);
    67     g_object_unref(mRootWindow);
    68     mRootWindow = nullptr;
    69   }
    71   /* XineramaIsActive() registers a callback function close_display()
    72    * in X, which is to be called in XCloseDisplay(). This is the case
    73    * if Xinerama is active, even if only with one screen.
    74    *
    75    * We can't unload libXinerama.so.1 here because this will make
    76    * the address of close_display() registered in X to be invalid and
    77    * it will crash when XCloseDisplay() is called later. */
    78 }
    81 // addref, release, QI
    82 NS_IMPL_ISUPPORTS(nsScreenManagerGtk, nsIScreenManager)
    85 // this function will make sure that everything has been initialized.
    86 nsresult
    87 nsScreenManagerGtk :: EnsureInit()
    88 {
    89   if (mCachedScreenArray.Count() > 0)
    90     return NS_OK;
    92   mRootWindow = gdk_get_default_root_window();
    93   g_object_ref(mRootWindow);
    95   // GDK_STRUCTURE_MASK ==> StructureNotifyMask, for ConfigureNotify
    96   // GDK_PROPERTY_CHANGE_MASK ==> PropertyChangeMask, for PropertyNotify
    97   gdk_window_set_events(mRootWindow,
    98                         GdkEventMask(gdk_window_get_events(mRootWindow) |
    99                                      GDK_STRUCTURE_MASK |
   100                                      GDK_PROPERTY_CHANGE_MASK));
   101   gdk_window_add_filter(mRootWindow, root_window_event_filter, this);
   102 #ifdef MOZ_X11
   103   mNetWorkareaAtom =
   104     XInternAtom(GDK_WINDOW_XDISPLAY(mRootWindow), "_NET_WORKAREA", False);
   105 #endif
   107   return Init();
   108 }
   110 nsresult
   111 nsScreenManagerGtk :: Init()
   112 {
   113 #ifdef MOZ_X11
   114   XineramaScreenInfo *screenInfo = nullptr;
   115   int numScreens;
   117   if (!mXineramalib) {
   118     mXineramalib = PR_LoadLibrary("libXinerama.so.1");
   119     if (!mXineramalib) {
   120       mXineramalib = SCREEN_MANAGER_LIBRARY_LOAD_FAILED;
   121     }
   122   }
   123   if (mXineramalib && mXineramalib != SCREEN_MANAGER_LIBRARY_LOAD_FAILED) {
   124     _XnrmIsActive_fn _XnrmIsActive = (_XnrmIsActive_fn)
   125         PR_FindFunctionSymbol(mXineramalib, "XineramaIsActive");
   127     _XnrmQueryScreens_fn _XnrmQueryScreens = (_XnrmQueryScreens_fn)
   128         PR_FindFunctionSymbol(mXineramalib, "XineramaQueryScreens");
   130     // get the number of screens via xinerama
   131     Display *display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
   132     if (_XnrmIsActive && _XnrmQueryScreens && _XnrmIsActive(display)) {
   133       screenInfo = _XnrmQueryScreens(display, &numScreens);
   134     }
   135   }
   137   // screenInfo == nullptr if either Xinerama couldn't be loaded or
   138   // isn't running on the current display
   139   if (!screenInfo || numScreens == 1) {
   140     numScreens = 1;
   141 #endif
   142     nsRefPtr<nsScreenGtk> screen;
   144     if (mCachedScreenArray.Count() > 0) {
   145       screen = static_cast<nsScreenGtk*>(mCachedScreenArray[0]);
   146     } else {
   147       screen = new nsScreenGtk();
   148       if (!screen || !mCachedScreenArray.AppendObject(screen)) {
   149         return NS_ERROR_OUT_OF_MEMORY;
   150       }
   151     }
   153     screen->Init(mRootWindow);
   154 #ifdef MOZ_X11
   155   }
   156   // If Xinerama is enabled and there's more than one screen, fill
   157   // in the info for all of the screens.  If that's not the case
   158   // then nsScreenGTK() defaults to the screen width + height
   159   else {
   160 #ifdef DEBUG
   161     printf("Xinerama superpowers activated for %d screens!\n", numScreens);
   162 #endif
   163     for (int i = 0; i < numScreens; ++i) {
   164       nsRefPtr<nsScreenGtk> screen;
   165       if (mCachedScreenArray.Count() > i) {
   166         screen = static_cast<nsScreenGtk*>(mCachedScreenArray[i]);
   167       } else {
   168         screen = new nsScreenGtk();
   169         if (!screen || !mCachedScreenArray.AppendObject(screen)) {
   170           return NS_ERROR_OUT_OF_MEMORY;
   171         }
   172       }
   174       // initialize this screen object
   175       screen->Init(&screenInfo[i]);
   176     }
   177   }
   178   // Remove any screens that are no longer present.
   179   while (mCachedScreenArray.Count() > numScreens) {
   180     mCachedScreenArray.RemoveObjectAt(mCachedScreenArray.Count() - 1);
   181   }
   183   if (screenInfo) {
   184     XFree(screenInfo);
   185   }
   186 #endif
   188   return NS_OK;
   189 }
   192 //
   193 // ScreenForRect 
   194 //
   195 // Returns the screen that contains the rectangle. If the rect overlaps
   196 // multiple screens, it picks the screen with the greatest area of intersection.
   197 //
   198 // The coordinates are in pixels (not app units) and in screen coordinates.
   199 //
   200 NS_IMETHODIMP
   201 nsScreenManagerGtk :: ScreenForRect ( int32_t aX, int32_t aY,
   202                                       int32_t aWidth, int32_t aHeight,
   203                                       nsIScreen **aOutScreen )
   204 {
   205   nsresult rv;
   206   rv = EnsureInit();
   207   if (NS_FAILED(rv)) {
   208     NS_ERROR("nsScreenManagerGtk::EnsureInit() failed from ScreenForRect");
   209     return rv;
   210   }
   211   // which screen ( index from zero ) should we return?
   212   uint32_t which = 0;
   213   // Optimize for the common case.  If the number of screens is only
   214   // one then this will fall through with which == 0 and will get the
   215   // primary screen.
   216   if (mCachedScreenArray.Count() > 1) {
   217     // walk the list of screens and find the one that has the most
   218     // surface area.
   219     uint32_t area = 0;
   220     nsIntRect windowRect(aX, aY, aWidth, aHeight);
   221     for (int32_t i = 0, i_end = mCachedScreenArray.Count(); i < i_end; ++i) {
   222       int32_t  x, y, width, height;
   223       x = y = width = height = 0;
   224       mCachedScreenArray[i]->GetRect(&x, &y, &width, &height);
   225       // calculate the surface area
   226       nsIntRect screenRect(x, y, width, height);
   227       screenRect.IntersectRect(screenRect, windowRect);
   228       uint32_t tempArea = screenRect.width * screenRect.height;
   229       if (tempArea >= area) {
   230         which = i;
   231         area = tempArea;
   232       }
   233     }
   234   }
   235   *aOutScreen = mCachedScreenArray.SafeObjectAt(which);
   236   NS_IF_ADDREF(*aOutScreen);
   237   return NS_OK;
   239 } // ScreenForRect
   242 //
   243 // GetPrimaryScreen
   244 //
   245 // The screen with the menubar/taskbar. This shouldn't be needed very
   246 // often.
   247 //
   248 NS_IMETHODIMP 
   249 nsScreenManagerGtk :: GetPrimaryScreen(nsIScreen * *aPrimaryScreen) 
   250 {
   251   nsresult rv;
   252   rv =  EnsureInit();
   253   if (NS_FAILED(rv)) {
   254     NS_ERROR("nsScreenManagerGtk::EnsureInit() failed from GetPrimaryScreen");
   255     return rv;
   256   }
   257   *aPrimaryScreen = mCachedScreenArray.SafeObjectAt(0);
   258   NS_IF_ADDREF(*aPrimaryScreen);
   259   return NS_OK;
   261 } // GetPrimaryScreen
   264 //
   265 // GetNumberOfScreens
   266 //
   267 // Returns how many physical screens are available.
   268 //
   269 NS_IMETHODIMP
   270 nsScreenManagerGtk :: GetNumberOfScreens(uint32_t *aNumberOfScreens)
   271 {
   272   nsresult rv;
   273   rv = EnsureInit();
   274   if (NS_FAILED(rv)) {
   275     NS_ERROR("nsScreenManagerGtk::EnsureInit() failed from GetNumberOfScreens");
   276     return rv;
   277   }
   278   *aNumberOfScreens = mCachedScreenArray.Count();
   279   return NS_OK;
   281 } // GetNumberOfScreens
   283 NS_IMETHODIMP
   284 nsScreenManagerGtk::GetSystemDefaultScale(float *aDefaultScale)
   285 {
   286   *aDefaultScale = 1.0f;
   287   return NS_OK;
   288 }
   290 NS_IMETHODIMP
   291 nsScreenManagerGtk :: ScreenForNativeWidget (void *aWidget, nsIScreen **outScreen)
   292 {
   293   nsresult rv;
   294   rv = EnsureInit();
   295   if (NS_FAILED(rv)) {
   296     NS_ERROR("nsScreenManagerGtk::EnsureInit() failed from ScreenForNativeWidget");
   297     return rv;
   298   }
   300   if (mCachedScreenArray.Count() > 1) {
   301     // I don't know how to go from GtkWindow to nsIScreen, especially
   302     // given xinerama and stuff, so let's just do this
   303     gint x, y, width, height;
   304 #if (MOZ_WIDGET_GTK == 2)
   305     gint depth;
   306 #endif
   307     x = y = width = height = 0;
   309 #if (MOZ_WIDGET_GTK == 2)
   310     gdk_window_get_geometry(GDK_WINDOW(aWidget), &x, &y, &width, &height,
   311                             &depth);
   312 #else
   313     gdk_window_get_geometry(GDK_WINDOW(aWidget), &x, &y, &width, &height);
   314 #endif
   315     gdk_window_get_origin(GDK_WINDOW(aWidget), &x, &y);
   316     rv = ScreenForRect(x, y, width, height, outScreen);
   317   } else {
   318     rv = GetPrimaryScreen(outScreen);
   319   }
   321   return rv;
   322 }

mercurial