image/decoders/icon/gtk/nsIconChannel.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/image/decoders/icon/gtk/nsIconChannel.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,666 @@
     1.4 +/* vim:set ts=2 sw=2 sts=2 cin et: */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include <stdlib.h>
    1.10 +#include <unistd.h>
    1.11 +
    1.12 +#include "mozilla/DebugOnly.h"
    1.13 +#include "mozilla/Endian.h"
    1.14 +#include <algorithm>
    1.15 +
    1.16 +#ifdef MOZ_ENABLE_GNOMEUI
    1.17 +// Older versions of these headers seem to be missing an extern "C"
    1.18 +extern "C" {
    1.19 +#include <libgnome/libgnome.h>
    1.20 +#include <libgnomeui/gnome-icon-theme.h>
    1.21 +#include <libgnomeui/gnome-icon-lookup.h>
    1.22 +
    1.23 +#include <libgnomevfs/gnome-vfs-file-info.h>
    1.24 +#include <libgnomevfs/gnome-vfs-ops.h>
    1.25 +}
    1.26 +#endif
    1.27 +#ifdef MOZ_ENABLE_GIO
    1.28 +#include <gio/gio.h>
    1.29 +#endif
    1.30 +
    1.31 +#include <gtk/gtk.h>
    1.32 +
    1.33 +#include "nsMimeTypes.h"
    1.34 +#include "nsIMIMEService.h"
    1.35 +
    1.36 +#include "nsIStringBundle.h"
    1.37 +
    1.38 +#include "nsNetUtil.h"
    1.39 +#include "nsIURL.h"
    1.40 +#include "prlink.h"
    1.41 +
    1.42 +#include "nsIconChannel.h"
    1.43 +
    1.44 +NS_IMPL_ISUPPORTS(nsIconChannel,
    1.45 +                  nsIRequest,
    1.46 +                  nsIChannel)
    1.47 +
    1.48 +#ifdef MOZ_ENABLE_GNOMEUI
    1.49 +// These let us have a soft dependency on libgnomeui rather than a hard one. These are just basically the prototypes
    1.50 +// of the functions in the libraries.
    1.51 +typedef char* (*_GnomeIconLookup_fn)(GtkIconTheme *icon_theme, GnomeThumbnailFactory *thumbnail_factory,
    1.52 +                                     const char *file_uri, const char *custom_icon, GnomeVFSFileInfo *file_info,
    1.53 +                                     const char *mime_type, GnomeIconLookupFlags flags, GnomeIconLookupResultFlags *result);
    1.54 +typedef GnomeIconTheme* (*_GnomeIconThemeNew_fn)(void);
    1.55 +typedef int (*_GnomeInit_fn)(const char *app_id, const char *app_version, int argc, char **argv, const struct poptOption *options,
    1.56 +                             int flags, poptContext *return_ctx);
    1.57 +typedef GnomeProgram* (*_GnomeProgramGet_fn)(void);
    1.58 +typedef GnomeVFSResult (*_GnomeVFSGetFileInfo_fn)(const gchar *text_uri, GnomeVFSFileInfo *info, GnomeVFSFileInfoOptions options);
    1.59 +typedef void (*_GnomeVFSFileInfoClear_fn)(GnomeVFSFileInfo *info);
    1.60 +
    1.61 +static PRLibrary* gLibGnomeUI = nullptr;
    1.62 +static PRLibrary* gLibGnome = nullptr;
    1.63 +static PRLibrary* gLibGnomeVFS = nullptr;
    1.64 +static bool gTriedToLoadGnomeLibs = false;
    1.65 +
    1.66 +static _GnomeIconLookup_fn _gnome_icon_lookup = nullptr;
    1.67 +static _GnomeIconThemeNew_fn _gnome_icon_theme_new = nullptr;
    1.68 +static _GnomeInit_fn _gnome_init = nullptr;
    1.69 +static _GnomeProgramGet_fn _gnome_program_get = nullptr;
    1.70 +static _GnomeVFSGetFileInfo_fn _gnome_vfs_get_file_info = nullptr;
    1.71 +static _GnomeVFSFileInfoClear_fn _gnome_vfs_file_info_clear = nullptr;
    1.72 +#endif //MOZ_ENABLE_GNOMEUI
    1.73 +
    1.74 +static nsresult
    1.75 +moz_gdk_pixbuf_to_channel(GdkPixbuf* aPixbuf, nsIURI *aURI,
    1.76 +                          nsIChannel **aChannel)
    1.77 +{
    1.78 +  int width = gdk_pixbuf_get_width(aPixbuf);
    1.79 +  int height = gdk_pixbuf_get_height(aPixbuf);
    1.80 +  NS_ENSURE_TRUE(height < 256 && width < 256 && height > 0 && width > 0 &&
    1.81 +                 gdk_pixbuf_get_colorspace(aPixbuf) == GDK_COLORSPACE_RGB &&
    1.82 +                 gdk_pixbuf_get_bits_per_sample(aPixbuf) == 8 &&
    1.83 +                 gdk_pixbuf_get_has_alpha(aPixbuf) &&
    1.84 +                 gdk_pixbuf_get_n_channels(aPixbuf) == 4,
    1.85 +                 NS_ERROR_UNEXPECTED);
    1.86 +
    1.87 +  const int n_channels = 4;
    1.88 +  gsize buf_size = 2 + n_channels * height * width;
    1.89 +  uint8_t * const buf = (uint8_t*)NS_Alloc(buf_size);
    1.90 +  NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
    1.91 +  uint8_t *out = buf;
    1.92 +
    1.93 +  *(out++) = width;
    1.94 +  *(out++) = height;
    1.95 +
    1.96 +  const guchar * const pixels = gdk_pixbuf_get_pixels(aPixbuf);
    1.97 +  int rowextra = gdk_pixbuf_get_rowstride(aPixbuf) - width * n_channels;
    1.98 +
    1.99 +  // encode the RGB data and the A data
   1.100 +  const guchar * in = pixels;
   1.101 +  for (int y = 0; y < height; ++y, in += rowextra) {
   1.102 +    for (int x = 0; x < width; ++x) {
   1.103 +      uint8_t r = *(in++);
   1.104 +      uint8_t g = *(in++);
   1.105 +      uint8_t b = *(in++);
   1.106 +      uint8_t a = *(in++);
   1.107 +#define DO_PREMULTIPLY(c_) uint8_t(uint16_t(c_) * uint16_t(a) / uint16_t(255))
   1.108 +#if MOZ_LITTLE_ENDIAN
   1.109 +      *(out++) = DO_PREMULTIPLY(b);
   1.110 +      *(out++) = DO_PREMULTIPLY(g);
   1.111 +      *(out++) = DO_PREMULTIPLY(r);
   1.112 +      *(out++) = a;
   1.113 +#else
   1.114 +      *(out++) = a;
   1.115 +      *(out++) = DO_PREMULTIPLY(r);
   1.116 +      *(out++) = DO_PREMULTIPLY(g);
   1.117 +      *(out++) = DO_PREMULTIPLY(b);
   1.118 +#endif
   1.119 +#undef DO_PREMULTIPLY
   1.120 +    }
   1.121 +  }
   1.122 +
   1.123 +  NS_ASSERTION(out == buf + buf_size, "size miscalculation");
   1.124 +
   1.125 +  nsresult rv;
   1.126 +  nsCOMPtr<nsIStringInputStream> stream =
   1.127 +    do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
   1.128 +  NS_ENSURE_SUCCESS(rv, rv);
   1.129 +
   1.130 +  rv = stream->AdoptData((char*)buf, buf_size);
   1.131 +  NS_ENSURE_SUCCESS(rv, rv);
   1.132 +
   1.133 +  rv = NS_NewInputStreamChannel(aChannel, aURI, stream,
   1.134 +                                NS_LITERAL_CSTRING(IMAGE_ICON_MS));
   1.135 +  return rv;
   1.136 +}
   1.137 +
   1.138 +static GtkWidget *gProtoWindow = nullptr;
   1.139 +static GtkWidget *gStockImageWidget = nullptr;
   1.140 +#ifdef MOZ_ENABLE_GNOMEUI
   1.141 +static GnomeIconTheme *gIconTheme = nullptr;
   1.142 +#endif //MOZ_ENABLE_GNOMEUI
   1.143 +
   1.144 +static void
   1.145 +ensure_stock_image_widget()
   1.146 +{
   1.147 +  // Only the style of the GtkImage needs to be used, but the widget is kept
   1.148 +  // to track dynamic style changes.
   1.149 +  if (!gProtoWindow) {
   1.150 +    gProtoWindow = gtk_window_new(GTK_WINDOW_POPUP);
   1.151 +    GtkWidget* protoLayout = gtk_fixed_new();
   1.152 +    gtk_container_add(GTK_CONTAINER(gProtoWindow), protoLayout);
   1.153 +
   1.154 +    gStockImageWidget = gtk_image_new();
   1.155 +    gtk_container_add(GTK_CONTAINER(protoLayout), gStockImageWidget);
   1.156 +
   1.157 +    gtk_widget_ensure_style(gStockImageWidget);
   1.158 +  }
   1.159 +}
   1.160 +
   1.161 +#ifdef MOZ_ENABLE_GNOMEUI
   1.162 +static nsresult
   1.163 +ensure_libgnomeui()
   1.164 +{
   1.165 +  // Attempt to get the libgnomeui symbol references. We do it this way so that stock icons from Init()
   1.166 +  // don't get held back by InitWithGnome()'s libgnomeui dependency.
   1.167 +  if (!gTriedToLoadGnomeLibs) {
   1.168 +    gLibGnomeUI = PR_LoadLibrary("libgnomeui-2.so.0");
   1.169 +    if (!gLibGnomeUI)
   1.170 +      return NS_ERROR_NOT_AVAILABLE;
   1.171 +
   1.172 +    _gnome_init = (_GnomeInit_fn)PR_FindFunctionSymbol(gLibGnomeUI, "gnome_init_with_popt_table");
   1.173 +    _gnome_icon_theme_new = (_GnomeIconThemeNew_fn)PR_FindFunctionSymbol(gLibGnomeUI, "gnome_icon_theme_new");
   1.174 +    _gnome_icon_lookup = (_GnomeIconLookup_fn)PR_FindFunctionSymbol(gLibGnomeUI, "gnome_icon_lookup");
   1.175 +
   1.176 +    if (!_gnome_init || !_gnome_icon_theme_new || !_gnome_icon_lookup) {
   1.177 +      PR_UnloadLibrary(gLibGnomeUI);
   1.178 +      gLibGnomeUI = nullptr;
   1.179 +      return NS_ERROR_NOT_AVAILABLE;
   1.180 +    }
   1.181 +  }
   1.182 +
   1.183 +  if (!gLibGnomeUI)
   1.184 +    return NS_ERROR_NOT_AVAILABLE;
   1.185 +
   1.186 +  return NS_OK;
   1.187 +}
   1.188 +
   1.189 +static nsresult
   1.190 +ensure_libgnome()
   1.191 +{
   1.192 +  if (!gTriedToLoadGnomeLibs) {
   1.193 +    gLibGnome = PR_LoadLibrary("libgnome-2.so.0");
   1.194 +    if (!gLibGnome)
   1.195 +      return NS_ERROR_NOT_AVAILABLE;
   1.196 +
   1.197 +    _gnome_program_get = (_GnomeProgramGet_fn)PR_FindFunctionSymbol(gLibGnome, "gnome_program_get");
   1.198 +    if (!_gnome_program_get) {
   1.199 +      PR_UnloadLibrary(gLibGnome);
   1.200 +      gLibGnome = nullptr;
   1.201 +      return NS_ERROR_NOT_AVAILABLE;
   1.202 +    }
   1.203 +  }
   1.204 +
   1.205 +  if (!gLibGnome)
   1.206 +    return NS_ERROR_NOT_AVAILABLE;
   1.207 +
   1.208 +  return NS_OK;
   1.209 +}
   1.210 +
   1.211 +static nsresult
   1.212 +ensure_libgnomevfs()
   1.213 +{
   1.214 +  if (!gTriedToLoadGnomeLibs) {
   1.215 +    gLibGnomeVFS = PR_LoadLibrary("libgnomevfs-2.so.0");
   1.216 +    if (!gLibGnomeVFS)
   1.217 +      return NS_ERROR_NOT_AVAILABLE;
   1.218 +
   1.219 +    _gnome_vfs_get_file_info = (_GnomeVFSGetFileInfo_fn)PR_FindFunctionSymbol(gLibGnomeVFS, "gnome_vfs_get_file_info");
   1.220 +    _gnome_vfs_file_info_clear = (_GnomeVFSFileInfoClear_fn)PR_FindFunctionSymbol(gLibGnomeVFS, "gnome_vfs_file_info_clear");
   1.221 +    if (!_gnome_vfs_get_file_info || !_gnome_vfs_file_info_clear) {
   1.222 +      PR_UnloadLibrary(gLibGnomeVFS);
   1.223 +      gLibGnomeVFS = nullptr;
   1.224 +      return NS_ERROR_NOT_AVAILABLE;
   1.225 +    }
   1.226 +  }
   1.227 +
   1.228 +  if (!gLibGnomeVFS)
   1.229 +    return NS_ERROR_NOT_AVAILABLE;
   1.230 +
   1.231 +  return NS_OK;
   1.232 +}
   1.233 +#endif //MOZ_ENABLE_GNOMEUI
   1.234 +
   1.235 +static GtkIconSize
   1.236 +moz_gtk_icon_size(const char *name)
   1.237 +{
   1.238 +  if (strcmp(name, "button") == 0)
   1.239 +    return GTK_ICON_SIZE_BUTTON;
   1.240 +
   1.241 +  if (strcmp(name, "menu") == 0)
   1.242 +    return GTK_ICON_SIZE_MENU;
   1.243 +
   1.244 +  if (strcmp(name, "toolbar") == 0)
   1.245 +    return GTK_ICON_SIZE_LARGE_TOOLBAR;
   1.246 +
   1.247 +  if (strcmp(name, "toolbarsmall") == 0)
   1.248 +    return GTK_ICON_SIZE_SMALL_TOOLBAR;
   1.249 +
   1.250 +  if (strcmp(name, "dnd") == 0)
   1.251 +    return GTK_ICON_SIZE_DND;
   1.252 +
   1.253 +  if (strcmp(name, "dialog") == 0)
   1.254 +    return GTK_ICON_SIZE_DIALOG;
   1.255 +
   1.256 +  return GTK_ICON_SIZE_MENU;
   1.257 +}
   1.258 +
   1.259 +#if defined(MOZ_ENABLE_GNOMEUI) || defined(MOZ_ENABLE_GIO)
   1.260 +static int32_t
   1.261 +GetIconSize(nsIMozIconURI *aIconURI)
   1.262 +{
   1.263 +  nsAutoCString iconSizeString;
   1.264 +
   1.265 +  aIconURI->GetIconSize(iconSizeString);
   1.266 +  if (iconSizeString.IsEmpty()) {
   1.267 +    uint32_t size;
   1.268 +    mozilla::DebugOnly<nsresult> rv = aIconURI->GetImageSize(&size);
   1.269 +    NS_ASSERTION(NS_SUCCEEDED(rv), "GetImageSize failed");
   1.270 +    return size; 
   1.271 +  } else {
   1.272 +    int size;
   1.273 +
   1.274 +    GtkIconSize icon_size = moz_gtk_icon_size(iconSizeString.get());
   1.275 +    gtk_icon_size_lookup(icon_size, &size, nullptr);
   1.276 +    return size;
   1.277 +  }
   1.278 +}
   1.279 +
   1.280 +/* Scale icon buffer to preferred size */
   1.281 +static nsresult
   1.282 +ScaleIconBuf(GdkPixbuf **aBuf, int32_t iconSize)
   1.283 +{
   1.284 +  // Scale buffer only if width or height differ from preferred size
   1.285 +  if (gdk_pixbuf_get_width(*aBuf)  != iconSize &&
   1.286 +      gdk_pixbuf_get_height(*aBuf) != iconSize) {
   1.287 +    GdkPixbuf *scaled = gdk_pixbuf_scale_simple(*aBuf, iconSize, iconSize,
   1.288 +                                                GDK_INTERP_BILINEAR);
   1.289 +    // replace original buffer by scaled
   1.290 +    g_object_unref(*aBuf);
   1.291 +    *aBuf = scaled;
   1.292 +    if (!scaled)
   1.293 +      return NS_ERROR_OUT_OF_MEMORY;
   1.294 +  }
   1.295 +  return NS_OK;
   1.296 +}
   1.297 +#endif
   1.298 +
   1.299 +#ifdef MOZ_ENABLE_GNOMEUI
   1.300 +nsresult
   1.301 +nsIconChannel::InitWithGnome(nsIMozIconURI *aIconURI)
   1.302 +{
   1.303 +  nsresult rv;
   1.304 +
   1.305 +  if (NS_FAILED(ensure_libgnomeui()) || NS_FAILED(ensure_libgnome()) || NS_FAILED(ensure_libgnomevfs())) {
   1.306 +    gTriedToLoadGnomeLibs = true;
   1.307 +    return NS_ERROR_NOT_AVAILABLE;
   1.308 +  }
   1.309 +
   1.310 +  gTriedToLoadGnomeLibs = true;
   1.311 +
   1.312 +  if (!_gnome_program_get()) {
   1.313 +    // Get the brandShortName from the string bundle to pass to GNOME
   1.314 +    // as the application name.  This may be used for things such as
   1.315 +    // the title of grouped windows in the panel.
   1.316 +    nsCOMPtr<nsIStringBundleService> bundleService = 
   1.317 +      do_GetService(NS_STRINGBUNDLE_CONTRACTID);
   1.318 +
   1.319 +    NS_ASSERTION(bundleService, "String bundle service must be present!");
   1.320 +
   1.321 +    nsCOMPtr<nsIStringBundle> bundle;
   1.322 +    bundleService->CreateBundle("chrome://branding/locale/brand.properties",
   1.323 +                                getter_AddRefs(bundle));
   1.324 +    nsAutoString appName;
   1.325 +
   1.326 +    if (bundle) {
   1.327 +      bundle->GetStringFromName(MOZ_UTF16("brandShortName"),
   1.328 +                                getter_Copies(appName));
   1.329 +    } else {
   1.330 +      NS_WARNING("brand.properties not present, using default application name");
   1.331 +      appName.Assign(NS_LITERAL_STRING("Gecko"));
   1.332 +    }
   1.333 +
   1.334 +    char* empty[] = { "" };
   1.335 +    _gnome_init(NS_ConvertUTF16toUTF8(appName).get(),
   1.336 +                "1.0", 1, empty, nullptr, 0, nullptr);
   1.337 +  }
   1.338 +
   1.339 +  uint32_t iconSize = GetIconSize(aIconURI);
   1.340 +  nsAutoCString type;
   1.341 +  aIconURI->GetContentType(type);
   1.342 +
   1.343 +  GnomeVFSFileInfo fileInfo = {0};
   1.344 +  fileInfo.refcount = 1; // In case some GnomeVFS function addrefs and releases it
   1.345 +
   1.346 +  nsAutoCString spec;
   1.347 +  nsCOMPtr<nsIURL> url;
   1.348 +  rv = aIconURI->GetIconURL(getter_AddRefs(url));
   1.349 +  if (url) {
   1.350 +    url->GetAsciiSpec(spec);
   1.351 +    // Only ask gnome-vfs for a GnomeVFSFileInfo for file: uris, to avoid a
   1.352 +    // network request
   1.353 +    bool isFile;
   1.354 +    if (NS_SUCCEEDED(url->SchemeIs("file", &isFile)) && isFile) {
   1.355 +      _gnome_vfs_get_file_info(spec.get(), &fileInfo, GNOME_VFS_FILE_INFO_DEFAULT);
   1.356 +    }
   1.357 +    else {
   1.358 +      // The filename we get is UTF-8-compatible, which matches gnome expectations.
   1.359 +      // See also: http://lists.gnome.org/archives/gnome-vfs-list/2004-March/msg00049.html
   1.360 +      // "Whenever we can detect the charset used for the URI type we try to
   1.361 +      //  convert it to/from utf8 automatically inside gnome-vfs."
   1.362 +      // I'll interpret that as "otherwise, this field is random junk".
   1.363 +      nsAutoCString name;
   1.364 +      url->GetFileName(name);
   1.365 +      fileInfo.name = g_strdup(name.get());
   1.366 +
   1.367 +      if (!type.IsEmpty()) {
   1.368 +        fileInfo.valid_fields = GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
   1.369 +        fileInfo.mime_type = g_strdup(type.get());
   1.370 +      }
   1.371 +    }
   1.372 +  }
   1.373 +
   1.374 +  if (type.IsEmpty()) {
   1.375 +    nsCOMPtr<nsIMIMEService> ms(do_GetService("@mozilla.org/mime;1"));
   1.376 +    if (ms) {
   1.377 +      nsAutoCString fileExt;
   1.378 +      aIconURI->GetFileExtension(fileExt);
   1.379 +      if (!fileExt.IsEmpty()) {
   1.380 +        ms->GetTypeFromExtension(fileExt, type);
   1.381 +      }
   1.382 +    }
   1.383 +  }
   1.384 +  // Get the icon theme
   1.385 +  if (!gIconTheme) {
   1.386 +    gIconTheme = _gnome_icon_theme_new();
   1.387 +
   1.388 +    if (!gIconTheme) {
   1.389 +      _gnome_vfs_file_info_clear(&fileInfo);
   1.390 +      return NS_ERROR_NOT_AVAILABLE;
   1.391 +    }
   1.392 +  }
   1.393 +
   1.394 +  char* name = _gnome_icon_lookup(gIconTheme, nullptr, spec.get(), nullptr,
   1.395 +                                  &fileInfo, type.get(),
   1.396 +                                  GNOME_ICON_LOOKUP_FLAGS_NONE, nullptr);
   1.397 +
   1.398 +  _gnome_vfs_file_info_clear(&fileInfo);
   1.399 +  if (!name)
   1.400 +    return NS_ERROR_NOT_AVAILABLE;
   1.401 +  
   1.402 +  // Get the default theme associated with the screen
   1.403 +  // Do NOT free.
   1.404 +  GtkIconTheme *theme = gtk_icon_theme_get_default();
   1.405 +  if (!theme) {
   1.406 +    g_free(name);
   1.407 +    return NS_ERROR_UNEXPECTED;
   1.408 +  }
   1.409 +
   1.410 +  GError *err = nullptr;
   1.411 +  GdkPixbuf* buf = gtk_icon_theme_load_icon(theme, name, iconSize, (GtkIconLookupFlags)0, &err);
   1.412 +  g_free(name);
   1.413 +
   1.414 +  if (!buf) {
   1.415 +    if (err)
   1.416 +      g_error_free(err);
   1.417 +    return NS_ERROR_UNEXPECTED;
   1.418 +  }
   1.419 +
   1.420 +  rv = ScaleIconBuf(&buf, iconSize);
   1.421 +  NS_ENSURE_SUCCESS(rv, rv);
   1.422 +
   1.423 +  rv = moz_gdk_pixbuf_to_channel(buf, aIconURI,
   1.424 +                                 getter_AddRefs(mRealChannel));
   1.425 +  g_object_unref(buf);
   1.426 +  return rv;
   1.427 +}
   1.428 +#endif // MOZ_ENABLE_GNOMEUI
   1.429 +
   1.430 +#ifdef MOZ_ENABLE_GIO
   1.431 +nsresult
   1.432 +nsIconChannel::InitWithGIO(nsIMozIconURI *aIconURI)
   1.433 +{
   1.434 +  GIcon *icon = nullptr;
   1.435 +  nsCOMPtr<nsIURL> fileURI;
   1.436 +
   1.437 +  // Read icon content
   1.438 +  aIconURI->GetIconURL(getter_AddRefs(fileURI));
   1.439 +
   1.440 +  // Get icon for file specified by URI
   1.441 +  if (fileURI) {
   1.442 +    bool isFile;
   1.443 +    nsAutoCString spec;
   1.444 +    fileURI->GetAsciiSpec(spec);
   1.445 +    if (NS_SUCCEEDED(fileURI->SchemeIs("file", &isFile)) && isFile) {
   1.446 +      GFile *file = g_file_new_for_uri(spec.get());
   1.447 +      GFileInfo *fileInfo = g_file_query_info(file,
   1.448 +                                              G_FILE_ATTRIBUTE_STANDARD_ICON,
   1.449 +                                              G_FILE_QUERY_INFO_NONE,
   1.450 +                                              nullptr, nullptr);
   1.451 +      g_object_unref(file);
   1.452 +      if (fileInfo) {
   1.453 +        // icon from g_content_type_get_icon doesn't need unref
   1.454 +        icon = g_file_info_get_icon(fileInfo);
   1.455 +        if (icon)
   1.456 +          g_object_ref(icon);
   1.457 +        g_object_unref(fileInfo);
   1.458 +      }
   1.459 +    }
   1.460 +  }
   1.461 +  
   1.462 +  // Try to get icon by using MIME type
   1.463 +  if (!icon) {
   1.464 +    nsAutoCString type;
   1.465 +    aIconURI->GetContentType(type);
   1.466 +    // Try to get MIME type from file extension by using nsIMIMEService
   1.467 +    if (type.IsEmpty()) {
   1.468 +      nsCOMPtr<nsIMIMEService> ms(do_GetService("@mozilla.org/mime;1"));
   1.469 +      if (ms) {
   1.470 +        nsAutoCString fileExt;
   1.471 +        aIconURI->GetFileExtension(fileExt);
   1.472 +        ms->GetTypeFromExtension(fileExt, type);
   1.473 +      }
   1.474 +    }
   1.475 +    char *ctype = nullptr; // character representation of content type
   1.476 +    if (!type.IsEmpty()) {
   1.477 +      ctype = g_content_type_from_mime_type(type.get());
   1.478 +    }
   1.479 +    if (ctype) {
   1.480 +      icon = g_content_type_get_icon(ctype);
   1.481 +      g_free(ctype);
   1.482 +    }
   1.483 +  }
   1.484 +
   1.485 +  // Get default icon theme
   1.486 +  GtkIconTheme *iconTheme = gtk_icon_theme_get_default();  
   1.487 +  GtkIconInfo *iconInfo = nullptr;
   1.488 +  // Get icon size
   1.489 +  int32_t iconSize = GetIconSize(aIconURI);
   1.490 +
   1.491 +  if (icon) {
   1.492 +    // Use icon and theme to get GtkIconInfo
   1.493 +    iconInfo = gtk_icon_theme_lookup_by_gicon(iconTheme,
   1.494 +                                              icon, iconSize,
   1.495 +                                              (GtkIconLookupFlags)0);
   1.496 +    g_object_unref(icon);
   1.497 +  }
   1.498 +  
   1.499 +  if (!iconInfo) {
   1.500 +    // Mozilla's mimetype lookup failed. Try the "unknown" icon.
   1.501 +    iconInfo = gtk_icon_theme_lookup_icon(iconTheme,
   1.502 +                                          "unknown", iconSize,
   1.503 +                                          (GtkIconLookupFlags)0);
   1.504 +    if (!iconInfo) {
   1.505 +      return NS_ERROR_NOT_AVAILABLE;
   1.506 +    }
   1.507 +  }
   1.508 +  
   1.509 +  // Create a GdkPixbuf buffer containing icon and scale it
   1.510 +  GdkPixbuf* buf = gtk_icon_info_load_icon(iconInfo, nullptr);
   1.511 +  gtk_icon_info_free(iconInfo);
   1.512 +  if (!buf) {
   1.513 +    return NS_ERROR_UNEXPECTED;
   1.514 +  }
   1.515 +  
   1.516 +  nsresult rv = ScaleIconBuf(&buf, iconSize);
   1.517 +  NS_ENSURE_SUCCESS(rv, rv);
   1.518 +
   1.519 +  rv = moz_gdk_pixbuf_to_channel(buf, aIconURI,
   1.520 +                                 getter_AddRefs(mRealChannel));
   1.521 +  g_object_unref(buf);
   1.522 +  return rv;
   1.523 +}
   1.524 +#endif // MOZ_ENABLE_GIO
   1.525 +
   1.526 +nsresult
   1.527 +nsIconChannel::Init(nsIURI* aURI)
   1.528 +{
   1.529 +  nsCOMPtr<nsIMozIconURI> iconURI = do_QueryInterface(aURI);
   1.530 +  NS_ASSERTION(iconURI, "URI is not an nsIMozIconURI");
   1.531 +
   1.532 +  nsAutoCString stockIcon;
   1.533 +  iconURI->GetStockIcon(stockIcon);
   1.534 +  if (stockIcon.IsEmpty()) {
   1.535 +#ifdef MOZ_ENABLE_GNOMEUI
   1.536 +    return InitWithGnome(iconURI);
   1.537 +#else 
   1.538 +#ifdef MOZ_ENABLE_GIO
   1.539 +    return InitWithGIO(iconURI);
   1.540 +#else
   1.541 +    return NS_ERROR_NOT_AVAILABLE;
   1.542 +#endif
   1.543 +#endif
   1.544 +  }
   1.545 +
   1.546 +  // Search for stockIcon
   1.547 +  nsAutoCString iconSizeString;
   1.548 +  iconURI->GetIconSize(iconSizeString);
   1.549 +
   1.550 +  nsAutoCString iconStateString;
   1.551 +  iconURI->GetIconState(iconStateString);
   1.552 +
   1.553 +  GtkIconSize icon_size = moz_gtk_icon_size(iconSizeString.get());
   1.554 +  GtkStateType state = iconStateString.EqualsLiteral("disabled") ?
   1.555 +    GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL;
   1.556 +
   1.557 +  // First lookup the icon by stock id and text direction.
   1.558 +  GtkTextDirection direction = GTK_TEXT_DIR_NONE;
   1.559 +  if (StringEndsWith(stockIcon, NS_LITERAL_CSTRING("-ltr"))) {
   1.560 +    direction = GTK_TEXT_DIR_LTR;
   1.561 +  } else if (StringEndsWith(stockIcon, NS_LITERAL_CSTRING("-rtl"))) {
   1.562 +    direction = GTK_TEXT_DIR_RTL;
   1.563 +  }
   1.564 +
   1.565 +  bool forceDirection = direction != GTK_TEXT_DIR_NONE;
   1.566 +  nsAutoCString stockID;
   1.567 +  bool useIconName = false;
   1.568 +  if (!forceDirection) {
   1.569 +    direction = gtk_widget_get_default_direction();
   1.570 +    stockID = stockIcon;
   1.571 +  } else {
   1.572 +    // GTK versions < 2.22 use icon names from concatenating stock id with
   1.573 +    // -(rtl|ltr), which is how the moz-icon stock name is interpreted here.
   1.574 +    stockID = Substring(stockIcon, 0, stockIcon.Length() - 4);
   1.575 +    // However, if we lookup bidi icons by the stock name, then GTK versions
   1.576 +    // >= 2.22 will use a bidi lookup convention that most icon themes do not
   1.577 +    // yet follow.  Therefore, we first check to see if the theme supports the
   1.578 +    // old icon name as this will have bidi support (if found).
   1.579 +    GtkIconTheme *icon_theme = gtk_icon_theme_get_default();
   1.580 +    // Micking what gtk_icon_set_render_icon does with sizes, though it's not
   1.581 +    // critical as icons will be scaled to suit size.  It just means we follow
   1.582 +    // the same pathes and so share caches.
   1.583 +    gint width, height;
   1.584 +    if (gtk_icon_size_lookup(icon_size, &width, &height)) {
   1.585 +      gint size = std::min(width, height);
   1.586 +      // We use gtk_icon_theme_lookup_icon() without
   1.587 +      // GTK_ICON_LOOKUP_USE_BUILTIN instead of gtk_icon_theme_has_icon() so
   1.588 +      // we don't pick up fallback icons added by distributions for backward
   1.589 +      // compatibility.
   1.590 +      GtkIconInfo *icon =
   1.591 +        gtk_icon_theme_lookup_icon(icon_theme, stockIcon.get(),
   1.592 +                                   size, (GtkIconLookupFlags)0);
   1.593 +      if (icon) {
   1.594 +        useIconName = true;
   1.595 +        gtk_icon_info_free(icon);
   1.596 +      }
   1.597 +    }
   1.598 +  }
   1.599 +
   1.600 +  ensure_stock_image_widget();
   1.601 +  GtkStyle *style = gtk_widget_get_style(gStockImageWidget);
   1.602 +  GtkIconSet *icon_set = nullptr;
   1.603 +  if (!useIconName) {
   1.604 +    icon_set = gtk_style_lookup_icon_set(style, stockID.get());
   1.605 +  }
   1.606 +
   1.607 +  if (!icon_set) {
   1.608 +    // Either we have choosen icon-name lookup for a bidi icon, or stockIcon is
   1.609 +    // not a stock id so we assume it is an icon name.
   1.610 +    useIconName = true;
   1.611 +    // Creating a GtkIconSet is a convenient way to allow the style to
   1.612 +    // render the icon, possibly with variations suitable for insensitive
   1.613 +    // states.
   1.614 +    icon_set = gtk_icon_set_new();
   1.615 +    GtkIconSource *icon_source = gtk_icon_source_new();
   1.616 +    
   1.617 +    gtk_icon_source_set_icon_name(icon_source, stockIcon.get());
   1.618 +    gtk_icon_set_add_source(icon_set, icon_source);
   1.619 +    gtk_icon_source_free(icon_source);
   1.620 +  }
   1.621 +
   1.622 +  GdkPixbuf *icon =
   1.623 +    gtk_icon_set_render_icon(icon_set, style, direction, state,
   1.624 +                             icon_size, gStockImageWidget, nullptr);
   1.625 +  if (useIconName) {
   1.626 +    gtk_icon_set_unref(icon_set);
   1.627 +  }
   1.628 +
   1.629 +  // According to documentation, gtk_icon_set_render_icon() never returns
   1.630 +  // nullptr, but it does return nullptr when we have the problem reported
   1.631 +  // here: https://bugzilla.gnome.org/show_bug.cgi?id=629878#c13
   1.632 +  if (!icon)
   1.633 +    return NS_ERROR_NOT_AVAILABLE;
   1.634 +  
   1.635 +  nsresult rv = moz_gdk_pixbuf_to_channel(icon, iconURI,
   1.636 +                                          getter_AddRefs(mRealChannel));
   1.637 +
   1.638 +  g_object_unref(icon);
   1.639 +
   1.640 +  return rv;
   1.641 +}
   1.642 +
   1.643 +void
   1.644 +nsIconChannel::Shutdown() {
   1.645 +  if (gProtoWindow) {
   1.646 +    gtk_widget_destroy(gProtoWindow);
   1.647 +    gProtoWindow = nullptr;
   1.648 +    gStockImageWidget = nullptr;
   1.649 +  }
   1.650 +#ifdef MOZ_ENABLE_GNOMEUI
   1.651 +  if (gIconTheme) {
   1.652 +    g_object_unref(G_OBJECT(gIconTheme));
   1.653 +    gIconTheme = nullptr;
   1.654 +  }
   1.655 +  gTriedToLoadGnomeLibs = false;
   1.656 +  if (gLibGnomeUI) {
   1.657 +    PR_UnloadLibrary(gLibGnomeUI);
   1.658 +    gLibGnomeUI = nullptr;
   1.659 +  }
   1.660 +  if (gLibGnome) {
   1.661 +    PR_UnloadLibrary(gLibGnome);
   1.662 +    gLibGnome = nullptr;
   1.663 +  }
   1.664 +  if (gLibGnomeVFS) {
   1.665 +    PR_UnloadLibrary(gLibGnomeVFS);
   1.666 +    gLibGnomeVFS = nullptr;
   1.667 +  }
   1.668 +#endif //MOZ_ENABLE_GNOMEUI
   1.669 +}

mercurial