image/decoders/icon/gtk/nsIconChannel.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* vim:set ts=2 sw=2 sts=2 cin et: */
     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 <stdlib.h>
     7 #include <unistd.h>
     9 #include "mozilla/DebugOnly.h"
    10 #include "mozilla/Endian.h"
    11 #include <algorithm>
    13 #ifdef MOZ_ENABLE_GNOMEUI
    14 // Older versions of these headers seem to be missing an extern "C"
    15 extern "C" {
    16 #include <libgnome/libgnome.h>
    17 #include <libgnomeui/gnome-icon-theme.h>
    18 #include <libgnomeui/gnome-icon-lookup.h>
    20 #include <libgnomevfs/gnome-vfs-file-info.h>
    21 #include <libgnomevfs/gnome-vfs-ops.h>
    22 }
    23 #endif
    24 #ifdef MOZ_ENABLE_GIO
    25 #include <gio/gio.h>
    26 #endif
    28 #include <gtk/gtk.h>
    30 #include "nsMimeTypes.h"
    31 #include "nsIMIMEService.h"
    33 #include "nsIStringBundle.h"
    35 #include "nsNetUtil.h"
    36 #include "nsIURL.h"
    37 #include "prlink.h"
    39 #include "nsIconChannel.h"
    41 NS_IMPL_ISUPPORTS(nsIconChannel,
    42                   nsIRequest,
    43                   nsIChannel)
    45 #ifdef MOZ_ENABLE_GNOMEUI
    46 // These let us have a soft dependency on libgnomeui rather than a hard one. These are just basically the prototypes
    47 // of the functions in the libraries.
    48 typedef char* (*_GnomeIconLookup_fn)(GtkIconTheme *icon_theme, GnomeThumbnailFactory *thumbnail_factory,
    49                                      const char *file_uri, const char *custom_icon, GnomeVFSFileInfo *file_info,
    50                                      const char *mime_type, GnomeIconLookupFlags flags, GnomeIconLookupResultFlags *result);
    51 typedef GnomeIconTheme* (*_GnomeIconThemeNew_fn)(void);
    52 typedef int (*_GnomeInit_fn)(const char *app_id, const char *app_version, int argc, char **argv, const struct poptOption *options,
    53                              int flags, poptContext *return_ctx);
    54 typedef GnomeProgram* (*_GnomeProgramGet_fn)(void);
    55 typedef GnomeVFSResult (*_GnomeVFSGetFileInfo_fn)(const gchar *text_uri, GnomeVFSFileInfo *info, GnomeVFSFileInfoOptions options);
    56 typedef void (*_GnomeVFSFileInfoClear_fn)(GnomeVFSFileInfo *info);
    58 static PRLibrary* gLibGnomeUI = nullptr;
    59 static PRLibrary* gLibGnome = nullptr;
    60 static PRLibrary* gLibGnomeVFS = nullptr;
    61 static bool gTriedToLoadGnomeLibs = false;
    63 static _GnomeIconLookup_fn _gnome_icon_lookup = nullptr;
    64 static _GnomeIconThemeNew_fn _gnome_icon_theme_new = nullptr;
    65 static _GnomeInit_fn _gnome_init = nullptr;
    66 static _GnomeProgramGet_fn _gnome_program_get = nullptr;
    67 static _GnomeVFSGetFileInfo_fn _gnome_vfs_get_file_info = nullptr;
    68 static _GnomeVFSFileInfoClear_fn _gnome_vfs_file_info_clear = nullptr;
    69 #endif //MOZ_ENABLE_GNOMEUI
    71 static nsresult
    72 moz_gdk_pixbuf_to_channel(GdkPixbuf* aPixbuf, nsIURI *aURI,
    73                           nsIChannel **aChannel)
    74 {
    75   int width = gdk_pixbuf_get_width(aPixbuf);
    76   int height = gdk_pixbuf_get_height(aPixbuf);
    77   NS_ENSURE_TRUE(height < 256 && width < 256 && height > 0 && width > 0 &&
    78                  gdk_pixbuf_get_colorspace(aPixbuf) == GDK_COLORSPACE_RGB &&
    79                  gdk_pixbuf_get_bits_per_sample(aPixbuf) == 8 &&
    80                  gdk_pixbuf_get_has_alpha(aPixbuf) &&
    81                  gdk_pixbuf_get_n_channels(aPixbuf) == 4,
    82                  NS_ERROR_UNEXPECTED);
    84   const int n_channels = 4;
    85   gsize buf_size = 2 + n_channels * height * width;
    86   uint8_t * const buf = (uint8_t*)NS_Alloc(buf_size);
    87   NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
    88   uint8_t *out = buf;
    90   *(out++) = width;
    91   *(out++) = height;
    93   const guchar * const pixels = gdk_pixbuf_get_pixels(aPixbuf);
    94   int rowextra = gdk_pixbuf_get_rowstride(aPixbuf) - width * n_channels;
    96   // encode the RGB data and the A data
    97   const guchar * in = pixels;
    98   for (int y = 0; y < height; ++y, in += rowextra) {
    99     for (int x = 0; x < width; ++x) {
   100       uint8_t r = *(in++);
   101       uint8_t g = *(in++);
   102       uint8_t b = *(in++);
   103       uint8_t a = *(in++);
   104 #define DO_PREMULTIPLY(c_) uint8_t(uint16_t(c_) * uint16_t(a) / uint16_t(255))
   105 #if MOZ_LITTLE_ENDIAN
   106       *(out++) = DO_PREMULTIPLY(b);
   107       *(out++) = DO_PREMULTIPLY(g);
   108       *(out++) = DO_PREMULTIPLY(r);
   109       *(out++) = a;
   110 #else
   111       *(out++) = a;
   112       *(out++) = DO_PREMULTIPLY(r);
   113       *(out++) = DO_PREMULTIPLY(g);
   114       *(out++) = DO_PREMULTIPLY(b);
   115 #endif
   116 #undef DO_PREMULTIPLY
   117     }
   118   }
   120   NS_ASSERTION(out == buf + buf_size, "size miscalculation");
   122   nsresult rv;
   123   nsCOMPtr<nsIStringInputStream> stream =
   124     do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
   125   NS_ENSURE_SUCCESS(rv, rv);
   127   rv = stream->AdoptData((char*)buf, buf_size);
   128   NS_ENSURE_SUCCESS(rv, rv);
   130   rv = NS_NewInputStreamChannel(aChannel, aURI, stream,
   131                                 NS_LITERAL_CSTRING(IMAGE_ICON_MS));
   132   return rv;
   133 }
   135 static GtkWidget *gProtoWindow = nullptr;
   136 static GtkWidget *gStockImageWidget = nullptr;
   137 #ifdef MOZ_ENABLE_GNOMEUI
   138 static GnomeIconTheme *gIconTheme = nullptr;
   139 #endif //MOZ_ENABLE_GNOMEUI
   141 static void
   142 ensure_stock_image_widget()
   143 {
   144   // Only the style of the GtkImage needs to be used, but the widget is kept
   145   // to track dynamic style changes.
   146   if (!gProtoWindow) {
   147     gProtoWindow = gtk_window_new(GTK_WINDOW_POPUP);
   148     GtkWidget* protoLayout = gtk_fixed_new();
   149     gtk_container_add(GTK_CONTAINER(gProtoWindow), protoLayout);
   151     gStockImageWidget = gtk_image_new();
   152     gtk_container_add(GTK_CONTAINER(protoLayout), gStockImageWidget);
   154     gtk_widget_ensure_style(gStockImageWidget);
   155   }
   156 }
   158 #ifdef MOZ_ENABLE_GNOMEUI
   159 static nsresult
   160 ensure_libgnomeui()
   161 {
   162   // Attempt to get the libgnomeui symbol references. We do it this way so that stock icons from Init()
   163   // don't get held back by InitWithGnome()'s libgnomeui dependency.
   164   if (!gTriedToLoadGnomeLibs) {
   165     gLibGnomeUI = PR_LoadLibrary("libgnomeui-2.so.0");
   166     if (!gLibGnomeUI)
   167       return NS_ERROR_NOT_AVAILABLE;
   169     _gnome_init = (_GnomeInit_fn)PR_FindFunctionSymbol(gLibGnomeUI, "gnome_init_with_popt_table");
   170     _gnome_icon_theme_new = (_GnomeIconThemeNew_fn)PR_FindFunctionSymbol(gLibGnomeUI, "gnome_icon_theme_new");
   171     _gnome_icon_lookup = (_GnomeIconLookup_fn)PR_FindFunctionSymbol(gLibGnomeUI, "gnome_icon_lookup");
   173     if (!_gnome_init || !_gnome_icon_theme_new || !_gnome_icon_lookup) {
   174       PR_UnloadLibrary(gLibGnomeUI);
   175       gLibGnomeUI = nullptr;
   176       return NS_ERROR_NOT_AVAILABLE;
   177     }
   178   }
   180   if (!gLibGnomeUI)
   181     return NS_ERROR_NOT_AVAILABLE;
   183   return NS_OK;
   184 }
   186 static nsresult
   187 ensure_libgnome()
   188 {
   189   if (!gTriedToLoadGnomeLibs) {
   190     gLibGnome = PR_LoadLibrary("libgnome-2.so.0");
   191     if (!gLibGnome)
   192       return NS_ERROR_NOT_AVAILABLE;
   194     _gnome_program_get = (_GnomeProgramGet_fn)PR_FindFunctionSymbol(gLibGnome, "gnome_program_get");
   195     if (!_gnome_program_get) {
   196       PR_UnloadLibrary(gLibGnome);
   197       gLibGnome = nullptr;
   198       return NS_ERROR_NOT_AVAILABLE;
   199     }
   200   }
   202   if (!gLibGnome)
   203     return NS_ERROR_NOT_AVAILABLE;
   205   return NS_OK;
   206 }
   208 static nsresult
   209 ensure_libgnomevfs()
   210 {
   211   if (!gTriedToLoadGnomeLibs) {
   212     gLibGnomeVFS = PR_LoadLibrary("libgnomevfs-2.so.0");
   213     if (!gLibGnomeVFS)
   214       return NS_ERROR_NOT_AVAILABLE;
   216     _gnome_vfs_get_file_info = (_GnomeVFSGetFileInfo_fn)PR_FindFunctionSymbol(gLibGnomeVFS, "gnome_vfs_get_file_info");
   217     _gnome_vfs_file_info_clear = (_GnomeVFSFileInfoClear_fn)PR_FindFunctionSymbol(gLibGnomeVFS, "gnome_vfs_file_info_clear");
   218     if (!_gnome_vfs_get_file_info || !_gnome_vfs_file_info_clear) {
   219       PR_UnloadLibrary(gLibGnomeVFS);
   220       gLibGnomeVFS = nullptr;
   221       return NS_ERROR_NOT_AVAILABLE;
   222     }
   223   }
   225   if (!gLibGnomeVFS)
   226     return NS_ERROR_NOT_AVAILABLE;
   228   return NS_OK;
   229 }
   230 #endif //MOZ_ENABLE_GNOMEUI
   232 static GtkIconSize
   233 moz_gtk_icon_size(const char *name)
   234 {
   235   if (strcmp(name, "button") == 0)
   236     return GTK_ICON_SIZE_BUTTON;
   238   if (strcmp(name, "menu") == 0)
   239     return GTK_ICON_SIZE_MENU;
   241   if (strcmp(name, "toolbar") == 0)
   242     return GTK_ICON_SIZE_LARGE_TOOLBAR;
   244   if (strcmp(name, "toolbarsmall") == 0)
   245     return GTK_ICON_SIZE_SMALL_TOOLBAR;
   247   if (strcmp(name, "dnd") == 0)
   248     return GTK_ICON_SIZE_DND;
   250   if (strcmp(name, "dialog") == 0)
   251     return GTK_ICON_SIZE_DIALOG;
   253   return GTK_ICON_SIZE_MENU;
   254 }
   256 #if defined(MOZ_ENABLE_GNOMEUI) || defined(MOZ_ENABLE_GIO)
   257 static int32_t
   258 GetIconSize(nsIMozIconURI *aIconURI)
   259 {
   260   nsAutoCString iconSizeString;
   262   aIconURI->GetIconSize(iconSizeString);
   263   if (iconSizeString.IsEmpty()) {
   264     uint32_t size;
   265     mozilla::DebugOnly<nsresult> rv = aIconURI->GetImageSize(&size);
   266     NS_ASSERTION(NS_SUCCEEDED(rv), "GetImageSize failed");
   267     return size; 
   268   } else {
   269     int size;
   271     GtkIconSize icon_size = moz_gtk_icon_size(iconSizeString.get());
   272     gtk_icon_size_lookup(icon_size, &size, nullptr);
   273     return size;
   274   }
   275 }
   277 /* Scale icon buffer to preferred size */
   278 static nsresult
   279 ScaleIconBuf(GdkPixbuf **aBuf, int32_t iconSize)
   280 {
   281   // Scale buffer only if width or height differ from preferred size
   282   if (gdk_pixbuf_get_width(*aBuf)  != iconSize &&
   283       gdk_pixbuf_get_height(*aBuf) != iconSize) {
   284     GdkPixbuf *scaled = gdk_pixbuf_scale_simple(*aBuf, iconSize, iconSize,
   285                                                 GDK_INTERP_BILINEAR);
   286     // replace original buffer by scaled
   287     g_object_unref(*aBuf);
   288     *aBuf = scaled;
   289     if (!scaled)
   290       return NS_ERROR_OUT_OF_MEMORY;
   291   }
   292   return NS_OK;
   293 }
   294 #endif
   296 #ifdef MOZ_ENABLE_GNOMEUI
   297 nsresult
   298 nsIconChannel::InitWithGnome(nsIMozIconURI *aIconURI)
   299 {
   300   nsresult rv;
   302   if (NS_FAILED(ensure_libgnomeui()) || NS_FAILED(ensure_libgnome()) || NS_FAILED(ensure_libgnomevfs())) {
   303     gTriedToLoadGnomeLibs = true;
   304     return NS_ERROR_NOT_AVAILABLE;
   305   }
   307   gTriedToLoadGnomeLibs = true;
   309   if (!_gnome_program_get()) {
   310     // Get the brandShortName from the string bundle to pass to GNOME
   311     // as the application name.  This may be used for things such as
   312     // the title of grouped windows in the panel.
   313     nsCOMPtr<nsIStringBundleService> bundleService = 
   314       do_GetService(NS_STRINGBUNDLE_CONTRACTID);
   316     NS_ASSERTION(bundleService, "String bundle service must be present!");
   318     nsCOMPtr<nsIStringBundle> bundle;
   319     bundleService->CreateBundle("chrome://branding/locale/brand.properties",
   320                                 getter_AddRefs(bundle));
   321     nsAutoString appName;
   323     if (bundle) {
   324       bundle->GetStringFromName(MOZ_UTF16("brandShortName"),
   325                                 getter_Copies(appName));
   326     } else {
   327       NS_WARNING("brand.properties not present, using default application name");
   328       appName.Assign(NS_LITERAL_STRING("Gecko"));
   329     }
   331     char* empty[] = { "" };
   332     _gnome_init(NS_ConvertUTF16toUTF8(appName).get(),
   333                 "1.0", 1, empty, nullptr, 0, nullptr);
   334   }
   336   uint32_t iconSize = GetIconSize(aIconURI);
   337   nsAutoCString type;
   338   aIconURI->GetContentType(type);
   340   GnomeVFSFileInfo fileInfo = {0};
   341   fileInfo.refcount = 1; // In case some GnomeVFS function addrefs and releases it
   343   nsAutoCString spec;
   344   nsCOMPtr<nsIURL> url;
   345   rv = aIconURI->GetIconURL(getter_AddRefs(url));
   346   if (url) {
   347     url->GetAsciiSpec(spec);
   348     // Only ask gnome-vfs for a GnomeVFSFileInfo for file: uris, to avoid a
   349     // network request
   350     bool isFile;
   351     if (NS_SUCCEEDED(url->SchemeIs("file", &isFile)) && isFile) {
   352       _gnome_vfs_get_file_info(spec.get(), &fileInfo, GNOME_VFS_FILE_INFO_DEFAULT);
   353     }
   354     else {
   355       // The filename we get is UTF-8-compatible, which matches gnome expectations.
   356       // See also: http://lists.gnome.org/archives/gnome-vfs-list/2004-March/msg00049.html
   357       // "Whenever we can detect the charset used for the URI type we try to
   358       //  convert it to/from utf8 automatically inside gnome-vfs."
   359       // I'll interpret that as "otherwise, this field is random junk".
   360       nsAutoCString name;
   361       url->GetFileName(name);
   362       fileInfo.name = g_strdup(name.get());
   364       if (!type.IsEmpty()) {
   365         fileInfo.valid_fields = GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
   366         fileInfo.mime_type = g_strdup(type.get());
   367       }
   368     }
   369   }
   371   if (type.IsEmpty()) {
   372     nsCOMPtr<nsIMIMEService> ms(do_GetService("@mozilla.org/mime;1"));
   373     if (ms) {
   374       nsAutoCString fileExt;
   375       aIconURI->GetFileExtension(fileExt);
   376       if (!fileExt.IsEmpty()) {
   377         ms->GetTypeFromExtension(fileExt, type);
   378       }
   379     }
   380   }
   381   // Get the icon theme
   382   if (!gIconTheme) {
   383     gIconTheme = _gnome_icon_theme_new();
   385     if (!gIconTheme) {
   386       _gnome_vfs_file_info_clear(&fileInfo);
   387       return NS_ERROR_NOT_AVAILABLE;
   388     }
   389   }
   391   char* name = _gnome_icon_lookup(gIconTheme, nullptr, spec.get(), nullptr,
   392                                   &fileInfo, type.get(),
   393                                   GNOME_ICON_LOOKUP_FLAGS_NONE, nullptr);
   395   _gnome_vfs_file_info_clear(&fileInfo);
   396   if (!name)
   397     return NS_ERROR_NOT_AVAILABLE;
   399   // Get the default theme associated with the screen
   400   // Do NOT free.
   401   GtkIconTheme *theme = gtk_icon_theme_get_default();
   402   if (!theme) {
   403     g_free(name);
   404     return NS_ERROR_UNEXPECTED;
   405   }
   407   GError *err = nullptr;
   408   GdkPixbuf* buf = gtk_icon_theme_load_icon(theme, name, iconSize, (GtkIconLookupFlags)0, &err);
   409   g_free(name);
   411   if (!buf) {
   412     if (err)
   413       g_error_free(err);
   414     return NS_ERROR_UNEXPECTED;
   415   }
   417   rv = ScaleIconBuf(&buf, iconSize);
   418   NS_ENSURE_SUCCESS(rv, rv);
   420   rv = moz_gdk_pixbuf_to_channel(buf, aIconURI,
   421                                  getter_AddRefs(mRealChannel));
   422   g_object_unref(buf);
   423   return rv;
   424 }
   425 #endif // MOZ_ENABLE_GNOMEUI
   427 #ifdef MOZ_ENABLE_GIO
   428 nsresult
   429 nsIconChannel::InitWithGIO(nsIMozIconURI *aIconURI)
   430 {
   431   GIcon *icon = nullptr;
   432   nsCOMPtr<nsIURL> fileURI;
   434   // Read icon content
   435   aIconURI->GetIconURL(getter_AddRefs(fileURI));
   437   // Get icon for file specified by URI
   438   if (fileURI) {
   439     bool isFile;
   440     nsAutoCString spec;
   441     fileURI->GetAsciiSpec(spec);
   442     if (NS_SUCCEEDED(fileURI->SchemeIs("file", &isFile)) && isFile) {
   443       GFile *file = g_file_new_for_uri(spec.get());
   444       GFileInfo *fileInfo = g_file_query_info(file,
   445                                               G_FILE_ATTRIBUTE_STANDARD_ICON,
   446                                               G_FILE_QUERY_INFO_NONE,
   447                                               nullptr, nullptr);
   448       g_object_unref(file);
   449       if (fileInfo) {
   450         // icon from g_content_type_get_icon doesn't need unref
   451         icon = g_file_info_get_icon(fileInfo);
   452         if (icon)
   453           g_object_ref(icon);
   454         g_object_unref(fileInfo);
   455       }
   456     }
   457   }
   459   // Try to get icon by using MIME type
   460   if (!icon) {
   461     nsAutoCString type;
   462     aIconURI->GetContentType(type);
   463     // Try to get MIME type from file extension by using nsIMIMEService
   464     if (type.IsEmpty()) {
   465       nsCOMPtr<nsIMIMEService> ms(do_GetService("@mozilla.org/mime;1"));
   466       if (ms) {
   467         nsAutoCString fileExt;
   468         aIconURI->GetFileExtension(fileExt);
   469         ms->GetTypeFromExtension(fileExt, type);
   470       }
   471     }
   472     char *ctype = nullptr; // character representation of content type
   473     if (!type.IsEmpty()) {
   474       ctype = g_content_type_from_mime_type(type.get());
   475     }
   476     if (ctype) {
   477       icon = g_content_type_get_icon(ctype);
   478       g_free(ctype);
   479     }
   480   }
   482   // Get default icon theme
   483   GtkIconTheme *iconTheme = gtk_icon_theme_get_default();  
   484   GtkIconInfo *iconInfo = nullptr;
   485   // Get icon size
   486   int32_t iconSize = GetIconSize(aIconURI);
   488   if (icon) {
   489     // Use icon and theme to get GtkIconInfo
   490     iconInfo = gtk_icon_theme_lookup_by_gicon(iconTheme,
   491                                               icon, iconSize,
   492                                               (GtkIconLookupFlags)0);
   493     g_object_unref(icon);
   494   }
   496   if (!iconInfo) {
   497     // Mozilla's mimetype lookup failed. Try the "unknown" icon.
   498     iconInfo = gtk_icon_theme_lookup_icon(iconTheme,
   499                                           "unknown", iconSize,
   500                                           (GtkIconLookupFlags)0);
   501     if (!iconInfo) {
   502       return NS_ERROR_NOT_AVAILABLE;
   503     }
   504   }
   506   // Create a GdkPixbuf buffer containing icon and scale it
   507   GdkPixbuf* buf = gtk_icon_info_load_icon(iconInfo, nullptr);
   508   gtk_icon_info_free(iconInfo);
   509   if (!buf) {
   510     return NS_ERROR_UNEXPECTED;
   511   }
   513   nsresult rv = ScaleIconBuf(&buf, iconSize);
   514   NS_ENSURE_SUCCESS(rv, rv);
   516   rv = moz_gdk_pixbuf_to_channel(buf, aIconURI,
   517                                  getter_AddRefs(mRealChannel));
   518   g_object_unref(buf);
   519   return rv;
   520 }
   521 #endif // MOZ_ENABLE_GIO
   523 nsresult
   524 nsIconChannel::Init(nsIURI* aURI)
   525 {
   526   nsCOMPtr<nsIMozIconURI> iconURI = do_QueryInterface(aURI);
   527   NS_ASSERTION(iconURI, "URI is not an nsIMozIconURI");
   529   nsAutoCString stockIcon;
   530   iconURI->GetStockIcon(stockIcon);
   531   if (stockIcon.IsEmpty()) {
   532 #ifdef MOZ_ENABLE_GNOMEUI
   533     return InitWithGnome(iconURI);
   534 #else 
   535 #ifdef MOZ_ENABLE_GIO
   536     return InitWithGIO(iconURI);
   537 #else
   538     return NS_ERROR_NOT_AVAILABLE;
   539 #endif
   540 #endif
   541   }
   543   // Search for stockIcon
   544   nsAutoCString iconSizeString;
   545   iconURI->GetIconSize(iconSizeString);
   547   nsAutoCString iconStateString;
   548   iconURI->GetIconState(iconStateString);
   550   GtkIconSize icon_size = moz_gtk_icon_size(iconSizeString.get());
   551   GtkStateType state = iconStateString.EqualsLiteral("disabled") ?
   552     GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL;
   554   // First lookup the icon by stock id and text direction.
   555   GtkTextDirection direction = GTK_TEXT_DIR_NONE;
   556   if (StringEndsWith(stockIcon, NS_LITERAL_CSTRING("-ltr"))) {
   557     direction = GTK_TEXT_DIR_LTR;
   558   } else if (StringEndsWith(stockIcon, NS_LITERAL_CSTRING("-rtl"))) {
   559     direction = GTK_TEXT_DIR_RTL;
   560   }
   562   bool forceDirection = direction != GTK_TEXT_DIR_NONE;
   563   nsAutoCString stockID;
   564   bool useIconName = false;
   565   if (!forceDirection) {
   566     direction = gtk_widget_get_default_direction();
   567     stockID = stockIcon;
   568   } else {
   569     // GTK versions < 2.22 use icon names from concatenating stock id with
   570     // -(rtl|ltr), which is how the moz-icon stock name is interpreted here.
   571     stockID = Substring(stockIcon, 0, stockIcon.Length() - 4);
   572     // However, if we lookup bidi icons by the stock name, then GTK versions
   573     // >= 2.22 will use a bidi lookup convention that most icon themes do not
   574     // yet follow.  Therefore, we first check to see if the theme supports the
   575     // old icon name as this will have bidi support (if found).
   576     GtkIconTheme *icon_theme = gtk_icon_theme_get_default();
   577     // Micking what gtk_icon_set_render_icon does with sizes, though it's not
   578     // critical as icons will be scaled to suit size.  It just means we follow
   579     // the same pathes and so share caches.
   580     gint width, height;
   581     if (gtk_icon_size_lookup(icon_size, &width, &height)) {
   582       gint size = std::min(width, height);
   583       // We use gtk_icon_theme_lookup_icon() without
   584       // GTK_ICON_LOOKUP_USE_BUILTIN instead of gtk_icon_theme_has_icon() so
   585       // we don't pick up fallback icons added by distributions for backward
   586       // compatibility.
   587       GtkIconInfo *icon =
   588         gtk_icon_theme_lookup_icon(icon_theme, stockIcon.get(),
   589                                    size, (GtkIconLookupFlags)0);
   590       if (icon) {
   591         useIconName = true;
   592         gtk_icon_info_free(icon);
   593       }
   594     }
   595   }
   597   ensure_stock_image_widget();
   598   GtkStyle *style = gtk_widget_get_style(gStockImageWidget);
   599   GtkIconSet *icon_set = nullptr;
   600   if (!useIconName) {
   601     icon_set = gtk_style_lookup_icon_set(style, stockID.get());
   602   }
   604   if (!icon_set) {
   605     // Either we have choosen icon-name lookup for a bidi icon, or stockIcon is
   606     // not a stock id so we assume it is an icon name.
   607     useIconName = true;
   608     // Creating a GtkIconSet is a convenient way to allow the style to
   609     // render the icon, possibly with variations suitable for insensitive
   610     // states.
   611     icon_set = gtk_icon_set_new();
   612     GtkIconSource *icon_source = gtk_icon_source_new();
   614     gtk_icon_source_set_icon_name(icon_source, stockIcon.get());
   615     gtk_icon_set_add_source(icon_set, icon_source);
   616     gtk_icon_source_free(icon_source);
   617   }
   619   GdkPixbuf *icon =
   620     gtk_icon_set_render_icon(icon_set, style, direction, state,
   621                              icon_size, gStockImageWidget, nullptr);
   622   if (useIconName) {
   623     gtk_icon_set_unref(icon_set);
   624   }
   626   // According to documentation, gtk_icon_set_render_icon() never returns
   627   // nullptr, but it does return nullptr when we have the problem reported
   628   // here: https://bugzilla.gnome.org/show_bug.cgi?id=629878#c13
   629   if (!icon)
   630     return NS_ERROR_NOT_AVAILABLE;
   632   nsresult rv = moz_gdk_pixbuf_to_channel(icon, iconURI,
   633                                           getter_AddRefs(mRealChannel));
   635   g_object_unref(icon);
   637   return rv;
   638 }
   640 void
   641 nsIconChannel::Shutdown() {
   642   if (gProtoWindow) {
   643     gtk_widget_destroy(gProtoWindow);
   644     gProtoWindow = nullptr;
   645     gStockImageWidget = nullptr;
   646   }
   647 #ifdef MOZ_ENABLE_GNOMEUI
   648   if (gIconTheme) {
   649     g_object_unref(G_OBJECT(gIconTheme));
   650     gIconTheme = nullptr;
   651   }
   652   gTriedToLoadGnomeLibs = false;
   653   if (gLibGnomeUI) {
   654     PR_UnloadLibrary(gLibGnomeUI);
   655     gLibGnomeUI = nullptr;
   656   }
   657   if (gLibGnome) {
   658     PR_UnloadLibrary(gLibGnome);
   659     gLibGnome = nullptr;
   660   }
   661   if (gLibGnomeVFS) {
   662     PR_UnloadLibrary(gLibGnomeVFS);
   663     gLibGnomeVFS = nullptr;
   664   }
   665 #endif //MOZ_ENABLE_GNOMEUI
   666 }

mercurial